zoukankan      html  css  js  c++  java
  • Uoj 73 未来程序

    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 o (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 imes 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-balabaladots) 的形式, (self) 做前缀.($ self-introduction,self-interested,self-absorbeddots$).

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

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

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

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

    Subtask 10

    • 仔细阅读发现,很多个函数在被调用,但没有互相调用对方或自己的情况(否则就死循环了).
    • 可以建成一个 (DAG) 图,那个最基础的函数权值为 (1) ,其他点处理一下,就可以算出答案.
    • 需要写一个读代码的程序.
  • 相关阅读:
    双指针算法_最长连续不重复子列长度
    前缀和_子矩阵的和
    前缀和
    高精度算法_大数除以小数
    高精度算法_大数乘小数
    高精度算法_大数相减
    高精度算法_大数加法
    一维差分矩阵
    二维差分矩阵
    整数二分
  • 原文地址:https://www.cnblogs.com/jklover/p/10433596.html
Copyright © 2011-2022 走看看