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:优美序列
来源:oschina
链接:https://my.oschina.net/u/4397875/blog/3440758