3671: [Noi2014]随机数生成器

来源:互联网 发布:局域网共享设置软件 编辑:程序博客网 时间:2024/05/21 08:01

其实这题真的很水。。。。。。一眼就能看出来解法。

但是

但是

但是

坑~坑~坑~坑~坑~坑啊。

首先模拟一遍出变换后的排列,时间O(N*M+Q),扫一遍排列,用x[i],y[i]记录排列中值为i的数在棋盘的(x[i],y[i])的位置,然后从1开始枚举,每次判断该数能否放在路径序列里,贪心一遍就可以了。

然而这里有两个坑:

①不能建x,y数组,三个5000*5000的数组就炸内存了,所以,设rank[i]为在排列T中值为i的数的下标,然后通过下标算出x,y。

②判断该数能否放在路径序列里,要判断两点,即该数在棋盘左下方不能有数(之前放上去的数),在右上方也不行,然后这里我很SX地用树状数组去算左下和右上有多少个数,O(N*M*logN*M)果断TLE了。其实直接暴力就可以了,每次在棋盘上放一个数的时候更新它的影响,即棋盘上不能再放数的位置。

这样就变成了O(N*M*N*M),Σ( ° △ °|||)︴好像有什么不对的地方。

哦对,加一个剪枝,在更新的时候如果该行(该列)不能更新,直接退出,因为更新的矩形必定是连续且一直到尽头的,所以这判断了后面的矩形有木有被更新,于是就变成了O(N*M)。

然后就过了。33秒多。。。。。。。。不过代码好短啊

#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int N=5000+5;typedef long long ll;int n,m,ans[N*2],rank[N*N],T[N][N];int a,b,c,d;void add(int x,int y){for(int i=x+1;i<=n;i++){bool flag=true;for(int j=y-1;j>=1;j--)if(T[i][j])break;else{T[i][j]=1;flag=false;}if(flag)break;}for(int i=x-1;i>=1;i--){bool flag=true;for(int j=y+1;j<=m;j++)if(T[i][j])break;else{T[i][j]=1;flag=false;}if(flag)break;}}inline int f(ll x){return (a*x*x+b*x+c)%d;}inline int x(int s){return (s-1)/m+1;}inline int y(int s){return (s-1)%m+1;}int main(){int last,q,u,v;scanf("%d%d%d%d%d%d%d%d",&last,&a,&b,&c,&d,&n,&m,&q);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)T[i][j]=(i-1)*m+j;for(int i=1;i<=n*m;i++){u=i;v=f(last)%i+1;swap(T[x(u)][y(u)],T[x(v)][y(v)]);last=f(last);}while(q--){scanf("%d%d",&u,&v);swap(T[x(u)][y(u)],T[x(v)][y(v)]);}for(int i=1;i<=n*m;i++)rank[T[x(i)][y(i)]]=i;int cnt=0;memset(T,0,sizeof(T));for(int i=1;i<=n*m;i++){if(!T[x(rank[i])][y(rank[i])]){ans[++cnt]=i;add(x(rank[i]),y(rank[i]));if(cnt==n+m-1)break;}}for(int i=1;i<=cnt;i++)if(i!=1)printf(" %d",ans[i]);else printf("%d",ans[i]);return 0;}


0 0
原创粉丝点击