noip模拟测试13
T1:矩阵游戏
刚看题一脸懵,感觉一点思路都没有……
然后就开始看题目,发现题目描述里似乎藏着什么东西???
感觉题目闲的无聊疯狂描述了一个极其简单的东西,好像在暗示着什么!
所以就把题目中的式子写下来——$(i-1)*m+j$
再用这个式子把答案表示出来——$\sum_{i=1}^nR_i\sum_{j=1}^mS_j[(i-1)*m+j]$
然后把含有$i$的项拆出来就可以$O(n)$做了
so,code
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<vector> 7 #define ll long long 8 using namespace std; 9 const int MAXN=1000005,MAXK=100005,D=1e9+7; 10 int n,m,k; 11 ll sums,sumr,s[MAXN],r[MAXN],ans,tmp,sum; 12 char opt[5]; 13 int main() { 14 scanf("%d%d%d",&n,&m,&k); 15 for(int i=1;i<=m;i++) s[i]=1; 16 for(int i=1;i<=n;i++) r[i]=1; 17 for(int i=1;i<=k;i++) { 18 ll x,y; 19 scanf("%s%lld%lld",opt,&x,&y); 20 if(opt[0]=='R') r[x]=r[x]*y%D; 21 else s[x]=s[x]*y%D; 22 } 23 for(int i=1;i<=m;i++) sums=(sums+s[i])%D; 24 for(int i=1;i<=m;i++) tmp=(tmp+s[i]*i)%D; 25 for(int i=1;i<=n;i++) ans=(ans+(tmp+(ll)(i-1)*m%D*sums)%D*r[i]%D)%D; 26 printf("%lld\n",(ans%D+D)%D); 27 return 0; 28 }
T2:跳房子
看到k的范围首先想到矩阵乘,但又看了看n和m还是算了
发现更改操作最多只会影响前面的三个点,于是就连边暴力跑,
又发现好像建出的图是n个点,n条边的有向图,那就必是内向的基环树森林
很容易想到当k很大的时候就是一直在环上绕,所以可以对环的长度取一下模,复杂度就差不多是$n*mlogk$
还是不太对,于是利用分块的思想(???)将每一行看作一个点
因为每走$m$次后必定会再回到第一行,所以可以定义一个$jump$数组
$jump[i]$表示从第$i$行第一列走m步后会到达第一列的哪一行
然后会发现$jump$数组构成的图也是内向的基环树森林
所以就可以利用分块的思想,小段暴力(move),大段分块(jump),再在jump的时候找循环节优化
这样查询的复杂度就可以做到$O(n+m)$
那修改呢?
通过手玩数据(???)我们可以发现,对于一个修改,最多会影响它前面3个点的路径
我们考虑其中的一个,对于一个路径改变的点,它的影响应该是这样的:
它只会影响到它之前的一个连续区间(当然这个区间也可以跨过边界)
为什么呢?因为当确定了某一列的一个连续区间一定会走到某点时
上一列中位于区间中部的一些点必定会走到区间内,所以也必定会走到某点
而位于区间两端的一些点则可能走向区间,也可能不走向区间
而正是这些两端的点决定了该行区间的拓展或是收缩
那么思路就很显然了我们只要在修改时维护一个区间
每次向前一列时只需要用当前区间两端的点来更新区间就好了
复杂度为$O(n+m)$
细节比较多,具体还是看代码吧
so,code
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<vector> 7 #define ll long long 8 using namespace std; 9 const int MAXN=4005,INF=0x7f7f7f7f; 10 int n,m,q,s[MAXN][MAXN],jump[MAXN],lst[MAXN],vis[MAXN],tim; 11 char opt[20]; 12 struct node { 13 int x,y; 14 }ver[MAXN][MAXN],now; 15 void add(int x,int y) { 16 int ny=y==m?1:y+1; 17 int nx[3]={x==1?n:x-1,x,x==n?1:x+1}; 18 int mxs=max(s[nx[0]][ny],max(s[nx[1]][ny],s[nx[2]][ny])); 19 if(mxs==s[nx[0]][ny]) 20 ver[x][y].x=nx[0],ver[x][y].y=ny; 21 else if(mxs==s[nx[1]][ny]) 22 ver[x][y].x=nx[1],ver[x][y].y=ny; 23 else ver[x][y].x=nx[2],ver[x][y].y=ny; 24 } 25 node move(node u,int k) { 26 if(!k) return u; 27 return move(ver[u.x][u.y],k-1); 28 } 29 void get_ver() { 30 for(int i=1;i<=n;i++) 31 for(int j=1;j<=m;j++) 32 add(i,j); 33 } 34 void get_jump() { 35 for(int i=1;i<=n;i++) { 36 node tmp; 37 tmp.x=i,tmp.y=1; 38 tmp=move(tmp,m); 39 jump[i]=tmp.x; 40 } 41 } 42 bool cirflag; 43 int jump_move(int x,int k,int t) { 44 if(!cirflag&&vis[x]==tim) k%=(t-lst[x]),cirflag=1; 45 if(!k) return x; 46 if(!cirflag) lst[x]=t; 47 vis[x]=tim; 48 return jump_move(jump[x],k-1,t+1); 49 } 50 int legal(int p,int o) { 51 if(!o) return ((p-1)%n+n)%n+1; 52 else return ((p-1)%m+m)%m+1; 53 } 54 int visit(int x,int y) { 55 int tmp=legal(x,0); 56 int nx=ver[tmp][y].x,ret; 57 if(tmp==1&&nx==n) ret=x-1; 58 else if(tmp==n&&nx==1) ret=x+1; 59 else ret=x+(nx-tmp); 60 return ret; 61 } 62 void change(int x,int y) { 63 x=legal(x,0); 64 y=legal(y,1); 65 node tmp;tmp.x=x,tmp.y=y; 66 tmp=move(tmp,m-y+1); 67 int mini=x,maxi=x,ny=y; 68 while(ny>1) { 69 --ny; 70 int tmpmi=INF,tmpmx=-INF; 71 for(int i=mini-1;i<=mini+1;i++) { 72 int to=visit(i,ny); 73 if(mini<=to&&to<=maxi) { 74 tmpmi=i; 75 break; 76 } 77 } 78 for(int i=maxi+1;i>=maxi-1;i--) { 79 int to=visit(i,ny); 80 if(mini<=to&&to<=maxi) { 81 tmpmx=i; 82 break; 83 } 84 } 85 if(tmpmi>tmpmx) return; 86 mini=tmpmi,maxi=tmpmx; 87 } 88 if(maxi-mini+1>=n) 89 for(int i=1;i<=n;i++) jump[i]=tmp.x; 90 else for(int i=mini;i<=maxi;i++) 91 jump[legal(i,0)]=tmp.x; 92 } 93 int main() { 94 scanf("%d%d",&n,&m); 95 for(int i=1;i<=n;i++) 96 for(int j=1;j<=m;j++) 97 scanf("%d",&s[i][j]); 98 get_ver(); 99 get_jump(); 100 scanf("%d",&q); 101 now.x=now.y=1; 102 for(int i=1,aa,bb,cc;i<=q;i++) { 103 scanf("%s",opt); 104 if(opt[0]=='c') { 105 scanf("%d%d%d",&aa,&bb,&cc); 106 s[aa][bb]=cc; 107 for(int i=aa-1;i<=aa+1;i++) 108 add(legal(i,0),legal(bb-1,1)); 109 for(int i=aa-1;i<=aa+1;i++) 110 change(i,bb-1); 111 } else { 112 scanf("%d",&cc); 113 while(cc&&now.y!=1) now=ver[now.x][now.y],--cc; 114 if(cc) { 115 ++tim;cirflag=0; 116 now.x=jump_move(now.x,cc/m,1),now.y=1; 117 now=move(now,cc%m); 118 } 119 printf("%d %d\n",now.x,now.y); 120 } 121 } 122 return 0; 123 }
ps:如果发现调细节心态崩了的话,可以看看另一种思路——线段树维护置换 ---->oiertkj
T3:优美序列