CSYZDay1模拟题解
T1.game
【问题描述】
LZK发明一个矩阵游戏,大家一起来玩玩吧,有一个N行M列的矩阵。第一行的数字是1,2,…M,第二行的数字是M+1,M+2…2*M,以此类推,第N行的数字是(N-1)*M+1,(N-1)*M+2…N*M。
例如,N=3,M=4的矩阵是这样的:
-
1
2
3
4
5
6
7
8
9
10
11
12
对于身为智慧之神的LZK来说,这个矩阵过于无趣.于是他决定改造这个矩阵,改造会进行K次,每次改造会将矩阵的某一行或某一列乘上一个数字,你的任务是计算最终这个矩阵内所有数字的和,输出答案对109+7取模。
【输入】
第一行包含三个正整数N、M、K,表示矩阵的大小与改造次数。接下来的行,每行会是如下两种形式之一:
R X Y,表示将矩阵的第X(1 ≤ X ≤ N)行变为原来的Y(0 ≤ Y ≤109)倍.
S X Y,表示将矩阵的第X(1 ≤ X ≤ M)列变为原来的Y(0 ≤ Y ≤)倍.
【输出】
输出一行一个整数,表示最终矩阵内所有元素的和对109+7取模的结果。
【数据范围】
40%的数据满足:1≤N,M≤1000;
80%的数据满足:1≤N,M≤1000000,1 ≤ K ≤1000;
100%的数据满足:1≤N,M≤1000000,1 ≤ K ≤100000。、
个人觉得T1还是比较简单的,因为这个矩阵最后的和与乘法操作的先后顺序没有关系,所以我们可以先把所有行的操作乘完,记录每行被乘的数取模之后的结果。之后选择把第一列的所有数加起来,后面每列与之相差的值就是每行被乘的倍数之和。这样的话线性相加之后每次再乘以这一列被乘的情况就可以啦。
看一下代码。千万要注意在linux下有字符读入的不要使用单个的getchar,否则在读取的时候会出错,可以选择使用cin,scanf或者多个getchar进行读入。
(这题在Linux下评测的话我原来代码会爆零,但是windows下是可以过的)
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 100005; const int N = 1000005; const ll mod = 1e9+7; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct ask { ll pos,val; }r[M],s[M]; ll n,m,k,tot1,tot2,cur,ans,sum; ll po[N],ro[N]; int main() { //freopen("game.in","r",stdin); //freopen("game.out","w",stdout); n = read(),m = read(),k = read(); rep(i,1,k) { char c; cin >> c; if(c == 'R') r[++tot1].pos = read(),r[tot1].val = read(); else if(c == 'S')s[++tot2].pos = read(),s[tot2].val = read(); } rep(i,1,n) po[i] = 1;rep(i,1,m) ro[i] = 1; rep(i,1,tot1) po[r[i].pos] *= r[i].val,po[r[i].pos] %= mod; rep(i,1,n) { ll p = 1 + (i-1) * m; p %= mod; cur += p * po[i],cur %= mod; sum += po[i]; if(sum >= mod) sum -= mod; } rep(i,1,tot2) ro[s[i].pos] *= s[i].val,ro[s[i].pos] %= mod; rep(i,1,m) { ans += cur * ro[i],ans %= mod; cur += sum; if(cur >= mod) cur -= mod; } printf("%lld\n",ans); return 0; }
T2.jump
【问题描述】
跳房子,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子是在N个格子上进行的,CYJ对游戏进行了改进,该成了跳棋盘,改进后的游戏是在一个N行M列的棋盘上进行,并规定从第一行往上可以走到最后一行,第一列往左可以走到最后一列,反之亦然。每个格子上有一个数字。
在这个棋盘左上角(1,1)放置着一枚棋子。每次棋子会走到右、右上和右下三个方向格子中对应上数字最大一个。即任意时刻棋子都只有一种走法,不存在多个格子同时满足条件。
现在有两种操作:
move
k 将棋子前进k步。
change
a b e 将第a行第b列格子上的数字修改为e。
请对于每一个move操作输出棋子移动完毕后所处的位置。
【输入】
第一行包含两个正整数N,M(3<=N,M<=2000),表示棋盘的大小。
接下来N行,每行M个整数,依次表示每个格子中的数字a[i,j](1<=
a[i,j]<=109)。
接下来一行包含一个正整数Q(1<=Q<=5000),表示操作次数。
接下来m行,每行一个操作,其中1<=a<=N,1<=b<=M,1<=k,e<=109。
【输出】
对于每个move操作,输出一行两个正整数x,y,即棋子所处的行号和列号。
【输入输出样例】
jump.in |
jump.out |
4 4 1 2 9 3 3 5 4 8 4 3 2 7 5 8 1 6 4 move 1 move 1 change 1 4 100 move 1 |
4 2 1 3 1 4
|
【数据范围】
10%的数据满足:3<= N,M <=50,Q<=5000,k<=10;
20%的数据满足:3<= N,M <=200,Q<=5000,k<=5000;
另有20%的数据满足:3<= N,M <=200,Q<=5000,k<=109;
100%的数据满足:3<= N,M <=2000,Q<=5000,e,k<=109;
这题当时想到了暴力的40分不过由于没有时间给写跪了。因为n×m内必出循环节(踩到一个走过的格子即进入循环节)
至于100分做法怎么做呢?正解的做法我看不大懂……所以使用了学姐的线段树维护置换的方法。我们记录对于每一列,在每一行其向下一列会走到哪。这样的话每一列都建立了一个置换,之后我们只要把他们全部乘起来就行。在行走的时候,如果步数够得话就直接用置换跳着走(每次走m列),否则一步一步走就可以。
至于修改操作,我们只需要修改这个点的左边,左上,左下三个点,之后直接在线段树内重新维护置换即可。
或许很难懂……看一下子恒dalao的代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 50005; const int N = 2005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,m,f[N][N]; char s[15]; struct node { int g[N]; node() { rep(i,1,n) g[i] = i; } node operator * (const node &b)const { node c; rep(i,1,n) c.g[i] = b.g[g[i]]; return c; } }nxt[N],t[N<<2]; node qpow(node k,int b) { node res; while(b) { if(b & 1) res = res * k; k = k * k; b >>= 1; } return res; } int pos(int x,bool flag)//pos函数用于确定取模之后的实际位置 { if(x == (flag ? m+1 : n+1)) return 1; if(!x) return flag ? m : n; return x; } void change(int x,int y) { int maxn = 0; x = pos(x,0),y = pos(y,1); rep(k,-1,1) { int kx = pos(k+x,0),ky = pos(y+1,1); if(maxn < f[kx][ky]) maxn = f[kx][ky],nxt[y].g[x] = kx; } } void build(int p,int l,int r) { if(l == r) { t[p] = nxt[l]; return; } int mid = (l+r) >> 1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); t[p] = t[p<<1] * t[p<<1|1]; } void modify(int p,int l,int r,int v) { if(l == r) { t[p] = nxt[v]; return; } int mid = (l+r) >> 1; if(v <= mid) modify(p<<1,l,mid,v); else modify(p<<1|1,mid+1,r,v); t[p] = t[p<<1] * t[p<<1|1]; } void move(int &x,int &y,int k) { while(k--) x = nxt[y].g[x],y = pos(y+1,1); } int main() { n = read(),m = read(); rep(i,1,n) rep(j,1,m) f[i][j] = read(); rep(i,1,n) rep(j,1,m) change(i,j); build(1,1,m); int q = read(),px = 1,py = 1; while(q--) { scanf("%s",s); if(s[0] == 'm') { int k = read(),len = min(k,m - py + 1); move(px,py,len),k -= len; if(k) px = qpow(t[1],k/m).g[px],k %= m,move(px,py,k); printf("%d %d\n",px,py); } else { int a = read(),b = read(),c = read(); f[a][b] = c; change(a-1,b-1),change(a,b-1),change(a+1,b-1); modify(1,1,m,pos(b-1,1)); } } return 0; }
T3.sequence
这题确实不会做了……考试的时候线段树无限暴力52pts。
正解也看不大懂……直接放出暴力代码吧。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 100005; const int INF = 1e9+7; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct seg { int v,minx,maxx; }t[M<<2]; int n,m,a[M],al,ar,pos[1005][1005],tot[M]; void build(int p,int l,int r) { if(l == r) { t[p].minx = t[p].maxx = a[l]; return; } int mid = (l+r) >> 1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); t[p].minx = min(t[p<<1].minx,t[p<<1|1].minx); t[p].maxx = max(t[p<<1].maxx,t[p<<1|1].maxx); } int query(int p,int l,int r,int kl,int kr,bool flag) { if(l == kl && r == kr) return flag? t[p].minx : t[p].maxx; int mid = (l+r) >> 1; if(kr <= mid) return query(p<<1,l,mid,kl,kr,flag); else if(kl > mid) return query(p<<1|1,mid+1,r,kl,kr,flag); else { if(flag) return min(query(p<<1,l,mid,kl,mid,flag),query(p<<1|1,mid+1,r,mid+1,kr,flag)); else return max(query(p<<1,l,mid,kl,mid,flag),query(p<<1|1,mid+1,r,mid+1,kr,flag)); } } void init() { rep(i,1,n) { rep(j,i+1,n) { int k1 = query(1,1,n,i,j,1),k2 = query(1,1,n,i,j,0); if(k2-k1+1 == j-i+1) pos[i][++tot[i]] = j; } } } void solve(int l,int r) { int cur = INF,pos1,pos2; per(i,l,1) { int k = lower_bound(pos[i]+1,pos[i]+1+tot[i],r) - pos[i]; if(k == tot[i]+1) continue; if(pos[i][k]-i+1 < cur) cur = pos[i][k]-l+1,pos1 = i,pos2 = pos[i][k]; } printf("%d %d\n",pos1,pos2); } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); n = read(); rep(i,1,n) a[i] = read(); build(1,1,n); init(); m = read(); rep(i,1,m) { al = read(),ar = read(); solve(al,ar); } return 0; }
这么长时间过去还是没有理解这题咋做……太弱了orz。
算是把坑填一下。