A. 矩阵游戏
是个水题,但是还是想了很长时间。
每个点对答案的贡献是$A[i][j]\times H[i]\times Z[j]$,
然后这个$sum$就是$\sum A[i][j]\times H[i]\times Z[j]$,
把$H[i]$提出来,也就是$\sum (H[i]\times \sum A[i][j]\times Z[j])$,
这个$A[i][j]$是有规律的,
这一行和上一行的差值就是$m\times \sum Z[j]$,
然后就可以逐行转移了。
最后是$O(n)$的。
丑陋的代码:
#include<algorithm> #include<iostream> #include<cstring> #include<string> #include<cstdio> #define Maxn 1000050 #define Reg register #define INF 0x7ffffff #define int long long #define mod 1000000007 using namespace std; int n,m,k,sum=0,ans=0,H[Maxn],Z[Maxn],dp[Maxn]; string s; signed main() { scanf("%lld%lld%lld",&n,&m,&k); for(Reg int i=1;i<=n;++i) H[i]=Z[i]=1; for(Reg int i=1;i<=m;++i) H[i]=Z[i]=1; for(Reg int i=1,x,y;i<=k;++i) { cin>>s; scanf("%lld%lld",&x,&y); if(s[0]=='R') H[x]=(H[x]*y)%mod; else Z[x]=(Z[x]*y)%mod; } for(Reg int i=1;i<=m;++i) { dp[1]=(dp[1]+i*Z[i]%mod)%mod; sum=(sum+Z[i])%mod; } for(Reg int i=2;i<=n;++i) dp[i]=(dp[i-1]+m*sum%mod)%mod; for(Reg int i=1;i<=n;++i) ans=(ans+H[i]*dp[i]%mod)%mod; printf("%lld",ans); return 0; }
B. 跳房子
C. 优美序列
一开始想的莫队(为啥他们都先想到分治),
想到可以向左右拓展,所以就朝莫队的方向想了。
最后发现,其实莫队还不如直接暴力。
开$2$颗线段树,一个是区间内的编号的最值,一个是一段编号的位置的最值。
每个询问直接找最远的两个值,向左向右拓展。
最后得到$80$分$TLE$。
丑陋的代码:
#include<algorithm> #include<iostream> #include<cstring> #include<string> #include<cstdio> #define int long long #define Maxn 100050 #define Reg register #define INF 0x7ffffff #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)<(y)?(x):(y)) using namespace std; int n,m,ansm,ansn,maxx,minn,A[Maxn],pos[Maxn]; struct Tree {Tree *lch,*rch; int mx,mn;}; Tree *New() { Tree *p=new Tree; p->lch=p->rch=NULL; p->mx=-INF,p->mn=INF; return p; } Tree *rot1=New(),*rot2=New(); void up(Tree *p) { p->mx=-INF,p->mn=INF; if(p->lch!=NULL) { p->mx=max(p->lch->mx,p->mx); p->mn=min(p->lch->mn,p->mn); } if(p->rch!=NULL) { p->mx=max(p->rch->mx,p->mx); p->mn=min(p->rch->mn,p->mn); } return; } void build(Tree **p,int l,int r) { if(*p==NULL) *p=New(); if(l==r) { (*p)->mx=(*p)->mn=pos[l]; return; } int mid=(l+r)>>1; if(l<=mid) build(&((*p)->lch),l,mid); if(mid+1<=r) build(&((*p)->rch),mid+1,r); up(*p); return; } void bup(Tree **p,int l,int r) { if(*p==NULL) *p=New(); if(l==r) { (*p)->mx=(*p)->mn=A[l]; return; } int mid=(l+r)>>1; if(l<=mid) bup(&((*p)->lch),l,mid); if(mid+1<=r) bup(&((*p)->rch),mid+1,r); up(*p); return; } void ask(Tree *p,int l,int r,int sol,int sor) { if(p==NULL||(p->mx<=ansm&&p->mn>=ansn)) return; if(sol<=l&&r<=sor) { ansm=max(ansm,p->mx); ansn=min(ansn,p->mn); return; } int mid=(l+r)>>1; if(sol<=mid) ask(p->lch,l,mid,sol,sor); if(mid+1<=sor) ask(p->rch,mid+1,r,sol,sor); return; } void query(Tree *p,int l,int r,int sol,int sor) { if(p==NULL||(p->mx<=maxx&&p->mn>=minn)) return; if(sol<=l&&r<=sor) { maxx=max(maxx,p->mx); minn=min(minn,p->mn); return; } int mid=(l+r)>>1; if(sol<=mid) query(p->lch,l,mid,sol,sor); if(mid+1<=sor) query(p->rch,mid+1,r,sol,sor); return; } signed main() { // freopen("text.in","r",stdin); scanf("%lld",&n); for(Reg int i=1;i<=n;++i) { scanf("%lld",&A[i]); pos[A[i]]=i; } build(&rot1,1,n); bup(&rot2,1,n); scanf("%lld",&m); for(Reg int i=1,l,r;i<=m;++i) { maxx=-INF,minn=INF; scanf("%lld%lld",&l,&r); query(rot2,1,n,l,r); int ln=l,rn=r; while(1) { if(maxx-minn==rn-ln||(ln==1&&rn==n)) { printf("%lld %lld\n",ln,rn); break; } ansm=-INF,ansn=INF; ask(rot1,1,n,minn,maxx); ln=ansn,rn=ansm; query(rot2,1,n,ln,rn); } } return 0; }
总结:
$T1$乍一看没什么思路,
刚开始一直在想怎么用线段树。
不管怎样都是$O(nm)$的复杂度。
然后就弃掉了。
最后码完$T3$有回来看$T1$,想了一会儿。
式子写下来,发现其实很水。逐行转移就可以了。
开始看$T2$,也没什么思路。
码了一个暴力(最后还打错了),先拿到这暴力分再说。
$T3$好像做过的样子,想到那个莫队专题里的$permu$,心态就炸了。
因为我没做那个题。
静下心来好好想,结果码了$2$颗线段树,然后水到$80$分。
最后回去看$T1$和$T2$,$T1$打完确实心态就稳了。
然后$T2$还是炸了,暴力分都没拿到。
所以最后$100+5+80=185$。
没什么水平。。。