Uoj 73 未来程序

神仙提答.

Subtask 1

  • 仔细阅读,发现是要计算 \(a*b\ \%\ c\).用龟速乘或者 \(python\) 直接算.

Subtask 2

  • 仔细阅读并手算一下,发现是每次令 \(a_{i+1}=a_i+2b_i+c_i,\ b_{i+1}=a_i+b_i,\ c_{i+1}=a_{i}\) ,重复 \(n\) 次.
  • 写一个矩阵快速幂来加速递推即可.

Subtask 3

  • 仔细阅读,发现是要计算 \(\sum_{i=0}^n i^k,k=0,1,2,3,4\) .众所周知,每个 \(k\) 的答案是一个关于 \(n\)\(k+1\) 次多项式.
  • 用拉格朗日插值法计算即可,\(k=4\) 时会溢出 \(ull\) ,为了简便用 \(python\) 计算,最后结果对 \(2^{64}\) 取模即可.

Subtask 4

  • 仔细阅读,发现给出了一个 \(5000*5000\)\(0/1\) 矩阵,有两种询问.

  • 第一种询问:为 \(1\) 的点两两组合,每对算 \(2\) 次的总数.显然为 \(s*(s-1)\) , \(s\)\(1\) 的个数.

  • 第二种询问:所有 \(1\) 到各自最近的 \(0\) 的曼哈顿距离总和,只需要将所有 \(0\) 的位置放入队列中, 做一遍 \(bfs\) .

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pii pair<int,int>
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			pos=0;
	for(;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
const int MAXN = 5010, inf = 0x3F3F3F3F;
int n, m, type;
bool data[MAXN + 11][MAXN + 11];
int seed;
int next_rand()
{
	static const int P = 1000000007, Q = 83978833, R = 8523467;
	return seed = ((long long)Q * seed % P * seed + R) % P;
}
void generate_input()
{
	cin >> n >> m >> type;
	for(int i = 0; i < n; i++)
		for(int j = 0; j < m; j++)
			data[i][j] = bool((next_rand() % 8) > 0);
}
ll count1()
{
	ll s=0;
	for(int i=0;i<n;++i)	
		for(int j=0;j<m;++j)
			s+=(int)data[i][j];
	return s*(s-1);
}
int dist[MAXN][MAXN],dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
queue<pii> q;
ll count2()
{
	for(int i=0;i<n;++i)
		for(int j=0;j<m;++j)
			if(data[i][j])
				dist[i][j]=inf;
			else
				dist[i][j]=0,q.push(mp(i,j));
	while(!q.empty())
		{
			int x=q.front().first,y=q.front().second;
			q.pop();
			for(int i=0;i<4;++i)
				{
					int nx=x+dx[i],ny=y+dy[i];
					if(nx<0 || nx>n || ny<0 || ny>m || dist[nx][ny]<inf)
						continue;
					dist[nx][ny]=dist[x][y]+1;
					q.push(mp(nx,ny));
				}
		}
	ll ans=0;
	for(int i=0;i<n;++i)
		for(int j=0;j<m;++j)
			ans+=dist[i][j];
	return ans;
}
int main()
{
	freopen("program4.in","r",stdin);
	freopen("program4.out","w",stdout);
	std::cin >> seed;
	for(int i = 0; i < 10; i++){
		generate_input();
		cout << (type == 1 ? count2() : count1()) << endl;
	}
	return 0;
}

Subtask 5

  • 仔细阅读,发现给出了一个 \(5000*5000\)\(0/1\) 矩阵,询问全为 \(1\) 的子矩阵个数.

  • \(0/1\) 矩阵相关问题常用单调栈解决.这道题也是,用经典的单调栈做法即可 \(O(n^2)\) 计算.

#include<bits/stdc++.h>

const int N = 5011;
int n, m;
bool data[N][N];

int seed;
int next_rand(){
	static const int P = 1000000007, Q = 83978833, R = 8523467;
	return seed = ((long long)Q * seed % P * seed + R) % P;
}

void generate_input(){
	std::cin >> n >> m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			data[i][j] = bool((next_rand() % 8) > 0);
}
int num[N][N];
int sta[N],up[N],down[N];
long long count3()
{
	long long ans=0;
	memset(num,0,sizeof num);
	memset(up,0,sizeof up);
	memset(down,0,sizeof down);
	for(int i=1;i<=n;++i)
    	for(int j=1;j<=m;++j)
    		num[i][j] =(data[i][j])?num[i][j-1]+1:0; 
    int top;
    for (int j=1;j<=m;++j)
    {
        top = 0;
        for (int i=1;i<=n;++i)
        {
            if(num[i][j])
	            {
	                up[i]=1;
	                while(top && num[i][j] <= num[sta[top]][j])
						up[i]+=up[sta[top]],--top;
	                sta[++top]=i;
	            }
            else 
				top=0,up[i]=0;
        }
        top = 0;
        for (int i=n;i>=1;--i)
        {
            if(num[i][j])
	            {
	                down[i]=1;
	                while(top && num[i][j] < num[sta[top]][j])
						down[i]+=down[sta[top]],--top;
	                sta[++top]=i;   
	            }
            else 
				top=0,down[i]=0;
            ans += 1LL*up[i]*down[i]*num[i][j];
        }
    }
	return ans;
}
int main(){
	freopen("program5.in","r",stdin);
	freopen("program5.out","w",stdout);
	std::cin >> seed;
	for(int i = 0; i < 10; i++)
		{
			generate_input();
			std::cout << count3() << std::endl;
		}
	return 0;
}

Subtask 6

  • 仔细阅读,发现给出了系数 \(n,a,b,c\) ,变量 \(t\) 初始为 \(0\) ,每次令 \(t=(t*t*a+b) \%\ c\) ,重复 \(n\) 次,要求最后的 \(t\) .
  • 硬算显然爆炸,而且一阶二次递推不一定有解析的通项公式,只好考虑找出循环节来求解.
  • \([0,c-1]\) 内所有数看成对应的节点,每个点连边 \(i\to (i*i*a+b) \%\ c\),则与 \(0\) 联通的部分是一颗基环内向树.
  • \(0\) 出发,走若干步后就会走到一个环上,进入循环.我们找出 \(0\) 与环的起始点距离与环的大小,即可减小 \(n\) 的规模.
  • 使用 \(Floyd\) 判圈算法,记两个指针 \(slow,fast\) ,初始都在 \(0\) ,每次让 \(slow\) 走一步, \(fast\) 走两步.
  • \(slow=fast\) 时,让 \(slow=0\) , \(fast\) 不变,然后每次让 \(slow,fast\) 都走一步,两者相遇的地方就是环的起点.
  • 此时,再让 \(slow\) 每次走一步, \(fast\) 每次走两步,当两者相遇时, \(slow\) 在这个阶段走过的步数就是环的大小.这样做的空间复杂度为 \(O(1)\),比 \(map,set\) 高明到不知道哪里去了.

虽然题答题无所谓...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			pos=0;
	for(;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
typedef unsigned long long ull;
ull n,a,b,c,t;
ull trans(ull x)
{
	return (x*x*a+b)%c;
}
int main()
{
	freopen("program6.in","r",stdin);
	freopen("program6.out","w",stdout);
	for(int T=1;T<=10;++T)
		{
			std::cin>>n>>a>>b>>c;
			if(T<=6)
				continue;
			ull s=0,cirsiz=0;
			t=0;
			ull slow=0,fast=0;
			while(1)
				{
					slow=trans(slow);
					fast=trans(fast);
					fast=trans(fast);
					if(slow==fast)
						break;
				}
			slow=0;
			while(1)
				{
					++s;
					slow=trans(slow);
					fast=trans(fast);
					if(slow==fast)
						break;
				}
			while(1)
				{
					++cirsiz;
					slow=trans(slow);
					fast=trans(fast);
					fast=trans(fast);
					if(slow==fast)
						break;
				}
			for(ull i=1;i<=s;++i)
				t=trans(t);
			if(n<=s)
				{
					cout<<t<<endl;
					continue;
				}
			n-=s;n%=cirsiz;
			for(ull i=1;i<=n;++i)
				t=trans(t);
			std::cout<<t<<endl;
		}
	return 0;
}

Subtask 7

  • 仔细阅读,发现是在求解一个 \(16\times 16\) 的数独.
  • \(DLX\) 求解即可.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			pos=0;
	for(;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
int point;
const int N = 17;
int mp[N][N];
int st[N][N];
int sum = 0;
char s[N][N+5];
void add(int x, int y, int t) {
    mp[x][y] = t;
    sum++;
    for (int i = 1; i <= 16; i++) 
		{
	        st[i][y] |= 1<<t-1;
	        st[x][i] |= 1<<t-1;
	    }
    int xx = (x+3)/4, yy = (y+3)/4;
    for (int i = (xx-1)*4 + 1; i <= xx*4; i++)
		{
	        for (int j = (yy-1)*4 + 1; j <= yy*4; j++)
				{
		            st[i][j] |= 1<<t-1;
		        }
		}
}
void print() 
{
	for(int k=1;k<=point;++k)
		{
			for (int i = 1; i <= 16; i++) 
				{
			        for (int j = 1; j <= 16; j++) 
						{
				            putchar(mp[i][j]-1+'A');
				        } 
			    }
			puts("");
		}
}
bool dfs() 
{
    if(sum == 256) 
		{
	        print();
	        return true;
	    }
    for (int i = 1; i <= 16; i++) 
	{
        for (int j = 1; j <= 16; j++) 
			{
	            if(!mp[i][j]) 
					{
		                int cnt = 0, t = 0;
		                for (int k = 1; k <= 16; k++) 
							{
			                    if((st[i][j] & (1<<k-1)) == 0) 
									{
				                        cnt++;
				                        t = k;
				                        if(cnt == 2) break;
				                    }
			                }
		                if(!cnt) 
							return false;
		                if(cnt == 1) 
							add(i, j, t);
		            }
	        }
    }

    for (int i = 1; i <= 16; i++) 
		{
	        for (int k = 1; k <= 16; k++) 
				{
		            int cnt1 = 0, cnt2 = 0, y;
		            for (int j = 1; j <= 16; j++) 
						{
			                if(mp[i][j] == k) 
								cnt1++;
			                if(cnt1 == 2) 
								return false;
			                if(!mp[i][j] && (st[i][j] & (1<<k-1)) == 0) 
								cnt2++, y = j;
			            }
		            if(!cnt1 && !cnt2) 
						return false;
		            if(!cnt1 && cnt2 == 1) 
						add(i, y, k);
		        }
	    }

    for (int j = 1; j <= 16; j++) 
		{
	        for (int k = 1; k <= 16; k++) 
				{
		            int cnt1 = 0, cnt2 = 0, x;
		            for (int i = 1; i <= 16; i++) 
						{
			                if(mp[i][j] == k) 
								cnt1++;
			                if(cnt1 == 2) 
								return false;
			                if(!mp[i][j] && (st[i][j] & (1<<k-1)) == 0) 
								cnt2++, x = i;
			            }
		            if(!cnt1 && !cnt2) 
						return false;
		            if(!cnt1 && cnt2 == 1) 
						add(x, j, k);
		        }
	    }

    for (int i = 1; i <= 16; i++) 
		{
	        int x = (i+3)/4, y = i - (x-1)*4;
	        for (int k = 1; k <= 16; k++) 
				{
		            int cnt1 = 0, cnt2 = 0, xx, yy;
		            for (int ii = (x-1)*4+1; ii <= x*4; ii++) 
						{
			                for (int jj = (y-1)*4+1; jj <= y*4; jj++) 
								{
				                    if(mp[ii][jj] == k) 
										cnt1++;
				                    if(cnt1 == 2) 
										return false;
				                    if(!mp[ii][jj] && (st[ii][jj] & (1<<k-1)) == 0) 
										cnt2++, xx = ii, yy = jj;
				                }
			            }
		            if(!cnt1 && !cnt2) 
						return false;
		            if(!cnt1 && cnt2 == 1) 
						add(xx, yy, k);
		        }
	    }
    if(sum == 256) 
		{
	        print();
	        return true;
		}
    int mn = N, x, y;
    for (int i = 1; i <= 16; i++) 
		{
	        for (int j = 1; j <= 16; j++) 
				{
		            if(!mp[i][j]) 
						{
			                int cnt = 0;
			                for (int k = 1; k <= 16; k++) 
								{
				                    if((st[i][j] & (1<<k-1)) == 0) 
										{
					                        cnt++;
					                        if(cnt >= mn) break;
					                    }
				                }
			                if(cnt < mn) 
								{
				                    mn = cnt;
				                    x = i;
				                    y = j;
				                }
			            }
		        }
	    }
    int tst[N][N], tmp[N][N];
    memcpy(tst, st, sizeof(st));
    memcpy(tmp, mp, sizeof(mp));
    int tsum = sum;
    for (int k = 1; k <= 16; k++) 
		{
	        if((st[x][y] & (1<<k-1)) == 0) 
				{
		            add(x, y, k);
		            bool f = dfs();
		            if(!f) 
						{
			                memcpy(st, tst, sizeof(tst));
			                memcpy(mp, tmp, sizeof(tmp));
			                sum = tsum;
			            }
		            else return true;
		        }
	    }
    return false;
}
int main() 
{
	freopen("program7.in","r",stdin);
	freopen("program7.out","w",stdout);
	for(point=1;point<=4;++point)
		{
	        for (int i = 1; i <= 16; i++) 
				{
		            scanf("%s", s[i]+1);
		        }
		        sum = 0;
	        memset(mp, 0, sizeof mp);
	        memset(st, 0, sizeof st);
	        for (int i = 1; i <= 16; i++) 
				{
		            for (int j = 1; j <= 16; j++) 
						{
			                if(isalpha(s[i][j])) 
								add(i, j, s[i][j] - 'A' + 1);
			            }
		        }
	        if(!dfs())
				{
					for(int i=1;i<=point;++i)
						puts("NO SOLUTION.");
				}
	    }
    return 0;
}

Subtask 8

  • 仔细阅读,发现不知道在讲什么...用程序算几个小的答案,差分一下,猜测答案是关于 \(n\) 的多项式?
  • 于是用拉格朗日插值计算即可.

Subtask 9

  • 仔细阅读,发现要回答猜出十个问题,每个问题的答案给出了 \(MD5\) 码,给出了暴力解码的程序.

  • 大体思路:以猜为主,实在猜不出来的就用给出的程序暴力解码.

  1. 据常识,为 \(1984\) .

*爷爷在 \(1984\) 年说:计算机普及要从娃娃抓起.同年, \(CCF\) 举办第一界 \(NOI\) .

  1. 据常识,为 \(123456\) .
  2. 显然\(chenlijie\).
  3. 提示作用不大,如果不知道怎么算 \(MD5\) ,就只能暴力解码了.否则大概可以枚举这三位算 \(MD5\) 看是否一致?反正我不太会算...暴力解出来是:(可以说是很形象的字符画了).

\[$\_$ \]

5~10: 有用的提示整理一下,就只有:

最后一个单词是带连字符号的,这7个单词拼起来是一句名言.

  • 这有啥用???如果去暴力解码最后 \(5\) 个点,显然 \(GG\) .那这提示咋用呢?

  • 打开 \(program10.cpp\) ,发现最后那一坨单词,似乎是美国的独立宣言?

  • 大胆猜测,这句名言就藏在这篇文章中.(不然那还做个p)

  • 大概可以把那篇文章处理成稍微可读一点的样子(全部小写单词,空格分隔).然后凭借强大的肉眼观察能力,或许可以发现 \(self\ evidence\) 这个东西很奇怪,联想到带 \(self\) 的大多是 \(self-balabala\dots\) 的形式, \(self\) 做前缀.($ self-introduction,self-interested,self-absorbed\dots$).

  • 加上连字符号,把前文带入一看, \(we\ hold\ these\ truths\ to\ be\ self-evident\),我们认为以下事实是不证自明的.

  • 恰好 \(7\) 个单词,最后一个单词带连字符号,也是一句名言,我们就钦定它是最后五个问题的答案了!

  • 大小写怎么办?可以暴力解码最后一个问题,解出来是 \(selfevident\) ,于是就肯定答案是正确的,且为小写.

  • 这样我们用 \(3\) 次暴力解码就完成了这道题.(或许 \(2\) 次)?

Subtask 10

  • 仔细阅读发现,很多个函数在被调用,但没有互相调用对方或自己的情况(否则就死循环了).
  • 可以建成一个 \(DAG\) 图,那个最基础的函数权值为 \(1\) ,其他点处理一下,就可以算出答案.
  • 需要写一个读代码的程序.

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-11-30
  • 2021-12-12
  • 2021-12-22
  • 2021-12-04
  • 2021-10-20
  • 2021-07-12
猜你喜欢
  • 2021-06-24
  • 2021-10-10
  • 2021-09-12
  • 2022-12-23
  • 2021-11-30
  • 2022-01-02
相关资源
相似解决方案