矩阵游戏
考试时思路一度和正解一样,考试到最后还是打了80分思路,结果80分打炸了只得了40分暴力分
题解
算出来第一列的总值,每次通过加每两列之间的差值得出下一列的总值
算第一列我们只需要让当前点*行增倍的数量就行了
for(ll i=1;i<=n;i++){ nowlie=(nowlie+elephant(i,1)*hang[i])%mod; sum=(sum+hang[i]); }
算其他列
nowlie=(nowlie+sum)%mod;
可能这一列会加倍只需乘上就行了
ans=(ans+nowlie*lie[i])%mod;
思路简单代码好打,然而考试我还是打炸了
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1010101 const ll mod=1e9+7; ll n,m,nowlie=0,sum=0,ans=0,k; ll lie[A],hang[A]; char c[5]; ll elephant(ll i,ll j){ return ((i-1)*m%mod+j)%mod; } int main(){ scanf("%lld%lld%lld",&n,&m,&k); for(ll i=1;i<=1000000;i++){ hang[i]=1; lie[i]=1; } for(ll i=1,a,x;i<=k;i++){ scanf("%s",c+1); scanf("%lld%lld",&a,&x); if(c[1]=='S'){ lie[a]=lie[a]*x%mod; } if(c[1]=='R'){ hang[a]=hang[a]*x%mod; } } for(ll i=1;i<=n;i++){ nowlie=(nowlie+elephant(i,1)*hang[i])%mod; sum=(sum+hang[i]); } for(ll i=1;i<=m;i++){ ans=(ans+nowlie*lie[i])%mod; nowlie=(nowlie+sum)%mod; } cout<<ans<<endl; }
t2,t3卡常题目,AC不了的
跳房子
看了一晚上第三题又看了扫描线,又打了一晚上,又理解了图论tarjan来做第三题,所以我用两个小时把t2A了???????
莫名和考试时思路相似 其实一点也不相似
$85\%$算法
暴力找循环节,剩下数据经过特殊构造你AC不了的,,,,,,,,,
代码
#include<bits/stdc++.h> using namespace std; #define ll long long char s[10]; #define A 11111111 ll vis[5100][5100],a[5100][5100]; inline int read(){ register int ret=0,f=1; register char r; r=getchar(); while(!isdigit(r)){ if(r=='-') f=-1; r=getchar(); } while(isdigit(r)){ ret=ret*10+r-'0'; r=getchar(); } return f*ret; } ll p,nowx=1,nowy=1,top=0,n,m; ll stax[A],stay[A]; void work(ll x){ ll tot=0; while(x){ ll nx=nowx,ny=nowy,d2=nx+1,d3=nx-1; (nx+1==n+1)?d2=1:d2=nx+1; (nx-1==0)?d3=n:d3=nx-1; if(ny==m) ny=0; ll z1=a[nx][ny+1],z2=a[d2][ny+1],z3=a[d3][ny+1]; if(z1>z2&&z1>z3) nowx=nx,nowy=ny+1; else if(z2>z1&&z2>z3) nowx=d2,nowy=ny+1; else if(z3>z1&&z3>z2) nowx=d3,nowy=ny+1; x--; top++;stax[top]=nowx,stay[top]=nowy; if(!vis[nowx][nowy]) vis[nowx][nowy]=top; else{ tot=top-vis[nowx][nowy]; x=x%tot; } } while(top){ ll x=stax[top],y=stay[top]; vis[x][y]=0; top--; } printf("%lld %lld\n",nowx,nowy); } int main(){ n=read(),m=read(); for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) a[i][j]=read(); p=read(); for(ll i=1,aa,bb,cc;i<=p;i++){ scanf("%s",s+1); if(s[1]=='m'){ cc=read(); work(cc); } if(s[1]=='c'){ aa=read(),bb=read(),cc=read(); a[aa][bb]=cc; } } }
$100\%$算法
和找循环节类似但又有很大区别,
思考循环节问题出现在那?
可能会遍历整张图才找到一个循环节,即使你预处理了找到循环节,那么出现change正好改掉循环节,再move找循环节,再change 再move你就被卡死了,复杂度本身就有问题
那么没办法做了吗
建立置换,走到一个点,如果步数大就直接置换,步数小就暴力走
我们用一个线段树来维护这个置换如果从1--m建树,那么t[1]就表示走m步置换成哪里
每次走m步
走的次数就是v/m
根据置换的运算$t^k$就是走了k次每次走t步
通过快速幂算出置换得出结果
那么我们经过%可以快速算出来剩下的,剩下的步数暴力走即可
顺便学了置换的运算
c.g[i]=a.g[t.g[i]];
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1010101 ll n,m,k,nowx=1,nowy=1; ll f[2100][2100]; char znsbc[10]; struct node{ ll g[2100]; node(){ for(ll i=1;i<=n;i++) g[i]=i; } node operator *(const node &a){ node t=*this,c; for(ll i=1;i<=n;i++) c.g[i]=a.g[t.g[i]]; return c; } }nxt[2100]; struct tree{ ll l,r,f; node t; }tr[10100]; inline void up(ll p){ tr[p].t=tr[p<<1].t*tr[p<<1|1].t; return ; } inline void built(ll p,ll l,ll r){ tr[p].l=l,tr[p].r=r; if(l==r){ tr[p].t=nxt[l]; return ; } ll mid=(l+r)>>1; built(p<<1,l,mid); built(p<<1|1,mid+1,r); up(p); } inline void add(ll p,ll o){ // printf("l=%lld r=%lld\n",l,r); if(tr[p].l==tr[p].r){ tr[p].t=nxt[o]; return ; } ll mid=(tr[p].l+tr[p].r)>>1; if(mid>=o) add(p<<1,o); else add(p<<1|1,o); up(p); } inline node meng(node x,ll k){ node ans; for(;k;k>>=1,x=x*x) if(k&1) ans=ans*x; return ans; } inline ll get(ll k,ll flag){ if(k==(flag?m+1:n+1)) return 1; if(!k) return flag?m:n; return k; } inline void change(ll xx,ll yy){ ll maxn=0; xx=get(xx,0); yy=get(yy,1); for(ll i=-1;i<=1;i++){ ll x=get(xx+i,0),y=get(yy+1,1); if(maxn<f[x][y]) maxn=f[x][y],nxt[yy].g[xx]=x; } return ; } inline void move(ll x){ while(x) x--,nowx=nxt[nowy].g[nowx],nowy=get(nowy+1,1)/*,printf("x=%lld\n",x)*/; } int main(){ scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) scanf("%lld",&f[i][j]); for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) change(i,j); built(1,1,m); scanf("%lld",&k); for(ll i=1,a,b,c,v,o;i<=k;i++){ scanf("%s",znsbc+1); if(znsbc[1]=='m'){ scanf("%lld",&v); ll len=min(v,m-nowy+1); move(len); v-=len; if(v){ nowx=meng(tr[1].t,v/m).g[nowx]; v%=m; if(v){ move(v); } } printf("%lld %lld\n",nowx,nowy); } else{ scanf("%lld%lld%lld",&a,&b,&c); f[a][b]=c; for(ll j=-1;j<=1;j++) change(a+j,b-1)/*,printf("*****\n");*/; o=get(b-1,1); add(1,o); } } }
随机数据生成
#include<bits/stdc++.h> #define ll long long using namespace std; ll random(ll n) { return rand()%n; } bool a[3210520]; int main() { freopen("mkd.txt","w",stdout); srand((unsigned)time(0)); ll n=random(5)+5,m=random(5)+5; printf("%lld %lld\n",n,m); for(ll i=1;i<=n;i++,puts("")) for(ll j=1;j<=m;j++){ ll x=random(550); while(a[x]) x=random(550); printf("%lld ",x); } ll k=random(50)+50; printf("%lld\n",k); for(ll i=1;i<=k;i++) { ll op=random(2); if(op) { printf("move "); printf("%lld\n",random(50000)+50000); } else{ printf("change "); ll l=random(n)+1; ll r=random(m)+1; printf("%lld %lld %lld\n",l,r,random(50)+50); } } fclose(stdout); }
t3做法尤其玄学
傻逼卡常题,傻逼卡常题,傻逼卡常题,毒瘤出题人,毒瘤数据,毒瘤做法
做法1,把序列问题转化为图论????????线段树优化建边+tarjan缩点+线段树维护图中内容我不会
做法2,扫描线,一条性质,若a<=b<=c<=d并且a--c是好区间,b--d是好区间,那么a--d是好区间
假设我们扫描到i i+1,那么如果i i+1在好区间里,那么val[i] val[i+1]都在好区间里,
设好的二元组为相邻两个数,那么区间若为好区间好二元组数量为r-l
用一棵线段树维护二元组数量设为v,若v+l=r则是好区间,假设我们当前扫描到了a[i],那么处于a[i]-1 a[i]+1的位置都要加1
线段树维护一下,细节比较多
做法3,性质若r-l=maxval-minval那么就是一个好区间
那么若maxval到minval之间全部出现那么是一个好区间,那么位置最左最右值出现即可,,
线段树维护一下||st表维护一下
但做法3本身复杂度不对,随机数据下表现优秀,但会被特殊数据卡
分块优化一下
#include<bits/stdc++.h> #define MAXN 100005 #define min(a,b) ((a<b)?(a):(b)) #define max(a,b) ((a>b)?(a):(b)) using namespace std; int mn[20][MAXN],mx[20][MAXN],mh[MAXN],a[MAXN],n,mnpos[20][MAXN],mxpos[20][MAXN],ans1[2005][2005],ans2[2005][2005],t; int bl[MAXN]; vector<int>ld; void pre() { for(int i=1;i<=n;i++) for(int j=17;j>=0;j--) if(i>=(1<<j)) { mh[i]=j; break; } for(int i=1;i<=17;++i) for(int j=1;j<=n;++j) { mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<(i-1))]); mx[i][j]=max(mx[i-1][j],mx[i-1][j+(1<<(i-1))]); mnpos[i][j]=min(mnpos[i-1][j],mnpos[i-1][j+(1<<(i-1))]); mxpos[i][j]=max(mxpos[i-1][j],mxpos[i-1][j+(1<<(i-1))]); } return ; } /*const int L=1<<20|1; char buffer[L],*S,*T; #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)*/ inline int Rd() { int x=0;char c=getchar(); while(c>'9'||c<'0')c=getchar(); while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();} return x; } inline int gmax(int l,int r) { return max(mx[mh[r-l+1]][l],mx[mh[r-l+1]][r-(1<<mh[r-l+1])+1]); } inline int gmin(int l,int r) { return min(mn[mh[r-l+1]][l],mn[mh[r-l+1]][r-(1<<mh[r-l+1])+1]); } inline int qmax(int l,int r) { return max(mxpos[mh[r-l+1]][l],mxpos[mh[r-l+1]][r-(1<<mh[r-l+1])+1]); } inline int qmin(int l,int r) { return min(mnpos[mh[r-l+1]][l],mnpos[mh[r-l+1]][r-(1<<mh[r-l+1])+1]); } int main() { // freopen("sequence21.in","r",stdin); n=Rd(); t=pow(n,0.7); for(int i=1;i<=n;i++) { a[i]=Rd(); mn[0][i]=mx[0][i]=a[i]; mnpos[0][a[i]]=mxpos[0][a[i]]=i; } int p=0,tot=0; while(p<n) { ld.push_back(p+1); for(int i=1;i<=t;i++)bl[p+i]=tot; p+=t; tot++; } pre(); memset(ans1,0x3f,sizeof(ans1)); memset(ans2,-0x3f,sizeof(ans2)); for(int i=0;i<ld.size();i++) for(int j=i;j<ld.size();j++) { int l,r; l=ld[i]; r=ld[j]; int nowmin=gmin(l,r),nowmax=gmax(l,r); int pl=qmin(nowmin,nowmax),pr=qmax(nowmin,nowmax); while(l>pl||r<pr) { if(l>pl) { nowmin=min(nowmin,gmin(pl,l)); nowmax=max(nowmax,gmax(pl,l)); l=pl; } if(r<pr) { nowmin=min(nowmin,gmin(r,pr)); nowmax=max(nowmax,gmax(r,pr)); r=pr; } pl=qmin(nowmin,nowmax);pr=qmax(nowmin,nowmax); } ans1[i][j]=l;ans2[i][j]=r; // cout<<ans1[i][j]<<' '<<ans2[i][j]<<endl; } int Q; Q=Rd(); while(Q--) { register int l,r,ll,rr; l=Rd();r=Rd(); ll=bl[l]+1;rr=bl[r]-1; int nowmin=gmin(l,r),nowmax=gmax(l,r); int pl=qmin(nowmin,nowmax),pr=qmax(nowmin,nowmax); while(l>pl||r<pr) { ll=bl[l]+1;rr=bl[r]-1; if(l>pl) { nowmin=min(nowmin,gmin(pl,l)); nowmax=max(nowmax,gmax(pl,l)); l=pl; l=min(l,ans1[ll][rr]); } if(r<pr) { nowmin=min(nowmin,gmin(r,pr)); nowmax=max(nowmax,gmax(r,pr)); r=pr; r=max(r,ans2[ll][rr]); } pl=qmin(nowmin,nowmax);pr=qmax(nowmin,nowmax); } printf("%d %d\n",l,r); } return 0; }