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 }
t1 Code

 


 

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 }
t2 Code

  ps:如果发现调细节心态崩了的话,可以看看另一种思路——线段树维护置换 ---->oiertkj 


 


 

T3:优美序列

 

 


 

posted @ 2019-08-05 11:58  G_keng  阅读(242)  评论(3编辑  收藏  举报