虽然凉了但是作业还是要做的。
Round 1(11.3)
T1:
我们首先需要观察出最后的$cnt(o)-cnt(x)=begin(o)-begin(x)$或$cnt(o)-cnt(x)=begin(o)-begin(x)+1$
1>$begin(o)>begin(x)$
这样我们是必胜的,所以尽快解决战斗即可。
这样优操作都是取最左边的能取的对方的棋子。
2>$begin(o)=begin(x)$
我们需要操作最后一步。
设$f(o)$为$pos(o)>left(x)$的点的个数,$f(x)$同理。
若$f(x)leq f(o)$显然我们可以每次操作最右边的$x$。
这样$f(o)$每轮减少1,保证了我们操作最后一步。
这样是必胜的。
反之$f(x)<f(o)$,我们操作完第一步之后对于对方是个相同的局面,我们必输。
中间有个地方很灵性。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i) #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i) using namespace std; typedef long long ll; #define M 105 int n,c; char s[M]; ll ans,f[M][M][M]; int main () { //freopen("a.in","r",stdin); scanf("%s%d",s+1,&c); n=strlen(s+1); f[0][0][0]=1; for1(1,n,i) { for1(0,i-1,j) for1(0,i-j-1,k) { ll sel=f[i-1][j][k]; if(!sel) continue; if(s[i]!='x') { if(k>1) f[i][j][k-1]+=sel; else f[i][j+1][k]+=sel; } if(s[i]!='o') { f[i][j][k+1]+=sel; } } } for1(c,n,i) for1(0,i-1,j) ans+=f[n][i][j]; memset(f,0,sizeof(f)); for1(1,n,i) { f[i][i-1][0]=1; for1(i,n,j) { for1(0,j-i,k) { ll sel=f[i][j-1][k]; if(!sel) continue; if(s[j]!='x') f[i][j][k+1]+=sel; if(s[j]!='o') f[i][j][k]+=sel; } } } if(n&1) return cout<<ans<<endl,0; for1(1,n,i) { if(s[i]=='x') break; FOR2(n,i+3,j) { if(s[j]=='o') break; if(s[i+1]=='o'||s[j-1]=='x') continue; int x=(n-2-2*i)/2; if(x<0||x>j-i-3||(j-i-3-x)>x) continue; if(n/2-(j-i-x-2)+1>=c) ans+=f[i+2][j-2][x]; } } cout<<ans<<endl; }
T2:
看到这个题你就会有种感觉要用到类欧几里得。
但是我并没有想出来怎么做,想出来也写不出来。
首先我们考虑将$(x,y)$变成$(x,x+y)$。
我们考虑一个在多边形内部的点,纵坐标在它之上的边的方向。
每次横坐标增大,都必须对应横坐标减小的边,而且还需要最后一条边包住它。
类似射线法求交点个数判是否在多边形内的方法。
然后就可以对每一条直线求然后再特判$(x,y)->(x,x+c)$以及边界上的点等等。
显然要搞成矩阵,然后自己推导一下发现可以化成如下式子:
$prod_{i=1}^{n}f^{left lfloor frac{a*i+b}{c} ight floor-left lfloor frac{a*i+b-a}{c} ight floor}*g$
1.$ageq c$
$prod_{i=1}^{n}f^{left lfloorfrac{(a\%c)*i+b}{c} ight floor-left lfloorfrac{(a\%c)*(i-1)+b}{c} ight floor+frac{a}{c}}*g$
2.$a<c$
$prod_{i=1}^{left lfloor frac{a*n+b}{c} ight floor}f*g^{left lfloor frac{i*c-b+a-1}{a} ight floor-left lfloor frac{(i-1)*c-b+a-1}{a} ight floor}=prod_{i=1}^{left lfloor frac{a*n+b}{c} ight floor}f*g^{left lfloor frac{c}{a} ight floor}*g^{left lfloor frac{i*(c\%a)-b+a-1}{a} ight floor-left lfloor frac{(i-1)*(c\%a)-b+a-1}{a} ight floor}$
这样显然符合类欧几里得的形式,然后递归求解就行了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i) #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i) using namespace std; const int N=20; const int mod=1e9+9; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } int n; int pre[N],x_[5]; bool ha[N]; struct node { int x,y; inline void in() { x=read(),y=x+read(); } inline int operator *(const node &a) { return x*a.y-y*a.x; } inline node operator -(const node &a) { return (node){x-a.x,y-a.y}; } }p[N]; inline int gcd(int x,int y) {return y?gcd(y,x%y):x;} inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} struct vec { int a[6]; inline void in() { FOR2(3,0,i) a[i]=read(); a[4]=a[3]; } }start_; struct mat { int a[6][6]; inline void init() { memset(a,0,sizeof(a)); } }a,b,tmp; inline mat operator *(mat x,mat y) { mat tmp; memset(tmp.a,0,sizeof(tmp.a)); for1(0,5,i) for1(0,5,j) for1(0,5,k) inc(tmp.a[i][k],1ll*x.a[i][j]*y.a[j][k]%mod); return tmp; } inline mat operator ^(mat x,int k) { mat sum; sum.init(); for1(0,5,i) sum.a[i][i]=1; for(;k;k>>=1,x=x*x) if(k&1) sum=sum*x; return sum; } inline vec operator *(vec x,mat y) { vec to; memset(to.a,0,sizeof(to.a)); for1(0,5,i) for1(0,5,j) inc(to.a[i],1ll*x.a[j]*y.a[j][i]%mod); return to; } inline int get_(bool vis,node x,node y,int i) { if(x.x>y.x) swap(x,y); ll c=1ll*i*(y.y-x.y)+1ll*x.y*(y.x-x.x); if(vis&&(c%(y.x-x.x)==0)) return c/(y.x-x.x)-1; return c/(y.x-x.x); } inline mat solve(int n,int m,mat a,mat b) { if(n==0) return b^m; else if(m==0) return a^n; else if(m>=n) return solve(n,m%n,a*(b^(m/n)),b); else return solve(n%m,m,a,(a^(n/m))*b); } inline int calc_(int t) { int ans=0; node x=p[t],y=p[t+1],las=p[pre[t]]; if(x.x>y.x) { ha[t]=las.x>x.x; if(x.y<y.y) inc(ans,(start_*(b^x.y)*solve(x.x-y.x,y.y-x.y,a,b)*a).a[5]); else inc(ans,(start_*(b^y.y)*solve(x.x-y.x,x.y-y.y,a,b)*a).a[5]); if(ha[t]) inc(ans,mod-(start_*(b^x.y)).a[4]); return ans; } else { int d; ha[t]=las.x<x.x; if(x.y<y.y) { inc(ans,(start_*(b^x.y)*solve(y.x-x.x,y.y-x.y,a,b)*a).a[5]); d=gcd(y.x-x.x,y.y-x.y); inc(ans,mod-(start_*(b^x.y)*tmp*(((b^((y.y-x.y)/d))*tmp)^d)).a[5]); } else { inc(ans,(start_*(b^y.y)*solve(y.x-x.x,x.y-y.y,a,b)*a).a[5]); d=gcd(y.x-x.x,x.y-y.y); inc(ans,mod-(start_*(b^y.y)*tmp*(((b^((x.y-y.y)/d))*tmp)^d)).a[5]); } if(ha[t]) inc(ans,mod-(start_*(b^(x.y-1))).a[4]); //去除整点。。。 return mod-ans; } } int main () { //freopen("a.in","r",stdin); int Test_=read(); while (Test_--) { n=read(); for1(0,3,i) x_[i]=read(); start_.in(); for1(1,n,i) p[i].in(); while (1) { int pos=0; for1(1,n,i) { int nxt=i%n+1; int nxtt=nxt%n+1; if((p[nxtt]-p[nxt])*(p[nxt]-p[i])==0) {pos=nxt;break;} } if(!pos) break; int cnt=0; for1(1,n,i) if(pos!=i) p[++cnt]=p[i]; n=cnt; } p[n+1]=p[1]; for1(1,n,i) { pre[i]=i; while (1) { --pre[i]; if(!pre[i]) pre[i]=n; if(p[i].x!=p[pre[i]].x) break; } } a.init(),b.init(); a.a[4][5]=1; for1(0,5,i) a.a[i][i]=1; for1(0,3,i) b.a[i][0]=x_[i]; b.a[0][1]=b.a[1][2]=b.a[2][3]=b.a[2][4]=b.a[4][4]=b.a[5][5]=1; tmp=a,tmp.a[4][5]=0,tmp.a[3][5]=1; int ans=0; for1(1,n,i) if(p[i].x!=p[i+1].x) inc(ans,calc_(i)); for1(1,n,i) { int cnt=0,num=0; int da=max(p[i].y,p[i+1].y); int xi=min(p[i].y,p[i+1].y); --da,++xi; for1(1,n,j) { if(ha[j]&&p[j].x==p[i].x) continue; int g[2]={p[j].x,p[j+1].x}; if(g[0]>g[1]) swap(g[0],g[1]); if(g[0]==g[1]||g[1]<p[i].x||g[0]>p[i].x) continue; int h=get_(p[j].x<p[j+1].x,p[j],p[j+1],p[i].x-g[0]); if(h>=da) num+=p[j].x<p[j+1].x?-1:1; if(h>=p[i].y) cnt+=p[j].x<p[j+1].x?-1:1; } inc(ans,1ll*(1-cnt+mod)*(start_*(b^p[i].y)).a[3]%mod); if(p[i].x==p[i+1].x&&da>=xi) inc(ans,1ll*(1-num)*((start_*(b^da)).a[4]-(start_*(b^(xi-1))).a[4]+mod)%mod); } printf("%d ",ans); } }
T3:
留坑。
Round 2(11.5)
T1:
真的是非常巧妙的一道题。
这个是没有办法贪心的,因为他选的顺序不是一步一步往深度小的走的。
首先有一个转化就是我们可以认为操作是选择一个点,减去权值。
并且当这个点的所有儿子都被选择之后可以加上这个点的权值,最后问最小值。
然后一个点就变成$(a_{i},b_{i})$代表先$a_{i}$的代价然后$b_{i}$的收益。
这个东西就是hdu6326了,不过这道题是转化完之后的弱化版。。。
通过hdu这个题,我们可以分类讨论出所有点的合并顺序(这个比较简单)。
然后问题就是我们需要对于每一个点都求出答案,而且要保证先选father的点后选son的。
其实就是一个简单的平衡树启发式合并了,一个点的平衡树代表子树内的合并顺序。
对于一个点$x$,合并完他的儿子之后我们需要让第一个点是包括$x$的点,就不断的那第一个比较大小合并即可。
因为不同子树内已经保证了先后关系,并且启发式合并不会改变大小关系,所以显然是对的了。
$O(n*log^2n)$。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i) #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 200005 int n; int a[M],fa[M]; ll sum[M],ans[M]; vector <int> e[M]; struct node { ll x,y; int id; inline bool operator <(const node &a) const { if((x<=y)!=(a.x<=a.y)) return x<=y; if(x<=y) return x==a.x?(y==a.y?id<a.id:y>a.y):x<a.x; return y==a.y?id<a.id:y>a.y; } }; inline node merge(node t,node h) { ll c=t.y-h.x; node to=(node){t.x,h.y,t.id}; c<0?to.x-=c:to.y+=c; return to; } struct SP { SP* ch[2]; int size; node sel,sum; inline SP(){} }*null,*root[M]; typedef pair<SP*,SP*> D; inline void update(SP* x) { x->size=x->ch[0]->size+x->ch[1]->size+1; if(x->ch[0]==null) x->sum=x->sel; else x->sum=merge(x->ch[0]->sum,x->sel); if(x->ch[1]!=null) x->sum=merge(x->sum,x->ch[1]->sum); } inline D split(SP* x,int k) { if(x==null) return D{null,null}; D y; int num=x->ch[0]->size; if(num>=k) { y=split(x->ch[0],k); x->ch[0]=y.second; update(x); y.second=x; } else { y=split(x->ch[1],k-num-1); x->ch[1]=y.first; update(x); y.first=x; } return y; } inline SP* Merge(SP* a,SP* b) { if(a==null) return b; if(b==null) return a; if(rand()%(a->size+b->size)<a->size) { a->ch[1]=Merge(a->ch[1],b); update(a); return a; } else { b->ch[0]=Merge(a,b->ch[0]); update(b); return b; } } inline node get_one(SP* x) { while (x->ch[0]!=null) x=x->ch[0]; return x->sel; } inline int get_rank(SP* x,node t) { int sum=0; while (x!=null) { if(!(x->sel<t)) x=x->ch[0]; else sum+=x->ch[0]->size+1,x=x->ch[1]; } return sum; } inline void insert(int x,node t) { int kth=get_rank(root[x],t); D y=split(root[x],kth); SP* o=new SP(); o->ch[0]=o->ch[1]=null; o->size=1,o->sel=o->sum=t; root[x]=Merge(y.first,Merge(o,y.second)); } inline void trans_(SP* o,int to) { if(o==null) return; trans_(o->ch[0],to); trans_(o->ch[1],to); insert(to,o->sel); o=NULL,delete(o); } inline void dfs(int x) { ll sum=0; root[x]=null; for(auto v:e[x]) { dfs(v); sum+=a[v]; if(root[v]->size>root[x]->size) swap(root[x],root[v]); trans_(root[v],x); } node t=(node){sum,a[x],x}; while (root[x]->size) { node h=get_one(root[x]); if(t<h) break; t=merge(t,h); root[x]=split(root[x],1).second; } insert(x,t); ans[x]=root[x]->sum.x+a[x]; } int main () { //freopen("a.in","r",stdin); //freopen("bl.out","w",stdout); read(); n=read(); for1(2,n,i) fa[i]=read(),e[fa[i]].push_back(i); for1(1,n,i) a[i]=read(); null=new SP(); null->size=0; null->ch[0]=null->ch[1]=null; dfs(1); for1(1,n,i) printf("%lld ",ans[i]); puts(""); }
T2:
坑,我还不会积分。。。
T3:
这个题其实挺简单的。
首先显然你可以搞出来每个点的多项式表达方式。(因为是很久之前的所以就不搞了)
我们是分开$f$,$a,b,c$求的。
其中最难搞的应该得从上一行推过来。
我就卡在了那里,其实你化一下式子就可以发现可以变成$x*g(i)=y*g(i)+z*g(i+1)+c$的形式。
然后移项搞一下就行了。
细节是真的多。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i) #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=1e6+10; int n,m,h,mod; int a,b,c,f[M],g[M],s[M],js[M],ni[M],inv[M]; inline int qpow(int x,int ci) { int sum=1; for(;ci;ci>>=1,x=1ll*x*x%mod) if(ci&1) sum=1ll*x*sum%mod; return sum; } inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline int cal1() { int ans=0; if(b==1&&h==1) { int t=1,k=0,a_i=1; FOR2(n,1,i) { t=1ll*t*(m+n-i)%mod*inv[n-i+1]%mod; inc(k,1ll*t*a_i%mod); a_i=1ll*a*a_i%mod; inc(ans,1ll*k*f[i]%mod); } } else { int k1=0,k2=0,c=1; int x=1ll*h*b%mod; int p1=qpow(h,(1ll*n*m-1)%(mod-1)); int p2=qpow(x,m+1); int p3=qpow((x+mod-1)%mod,mod-2); int p4=1,p5=qpow(qpow(h,m),mod-2); FOR2(n,1,i) { if(i!=n) k2=(1ll*c*p2%mod-k2+mod)*p3%mod; else k2=1ll*(qpow(x,m+1)-x+mod)*qpow((x+mod-1)%mod,mod-2)%mod; k1=1ll*(k1+1ll*p1*p4%mod*k2%mod)*p5%mod; inc(ans,1ll*k1*f[i]%mod); p4=1ll*p4*a%mod; c=1ll*c*(m+n-i)%mod*inv[n-i+1]%mod; } } return ans; } inline int cal2() { if(!c) return 0; if(b==1&&h==1) { g[0]=m; for1(1,n,i) g[i]=1ll*g[i-1]*(m+i)%mod*inv[i+1]%mod; } else { int x=1ll*b*h%mod; g[0]=1ll*(qpow(x,m)+mod-1)*qpow(x-1,mod-2)%mod; int c=1; int x_m=qpow(x,m); int p1=qpow((x+mod-1)%mod,mod-2); for1(1,n,i) { c=1ll*c*(i+m-1)%mod*inv[i]%mod; g[i]=(1ll*x_m*c%mod-g[i-1]+mod)*p1%mod; } } /* for1(0,n,x) { int sum=0; for1(0,m-1,i) inc(sum,1ll*qpow(b,i)*js[i+x]%mod*ni[x]%mod*ni[i]%mod*qpow(h,i)%mod); cout<<sum<<" "<<g[x]<<endl; } */ //cout<<h<<" "<<b<<endl; if(b==1) { for1(0,n,i) s[i]=g[i+1]; } else { if(h!=1) { int x=b; if(x==1) s[0]=1ll*m*qpow(h,m)%mod; else s[0]=1ll*(qpow(b,m)-1+mod)*qpow((b+mod-1)%mod,mod-2)%mod*qpow(h,m)%mod; x=1ll*h*b%mod; inc(s[0],mod-1ll*(qpow(x,m)+mod-1)*qpow((x+mod-1)%mod,mod-2)%mod); s[0]=1ll*s[0]*qpow((h+mod-1)%mod,mod-2)%mod; } else { if(b==1) s[0]=(1ll*m*m%mod-1ll*m*(m-1)%mod*inv[2]%mod+mod)%mod; else { s[0]=1ll*m*(qpow(b,m)+mod-1)%mod; inc(s[0],mod-1ll*(m-1)*qpow(b,m)%mod); inc(s[0],1ll*(qpow(b,m)-b+mod)*qpow(b-1,mod-2)%mod); s[0]=1ll*s[0]*qpow((b+mod-1)%mod,mod-2)%mod; } } int p1=qpow((b+mod-1)%mod,mod-2); for1(1,n,i) s[i]=(1ll*b*g[i]%mod-s[i-1]+mod)*p1%mod; } /* for1(0,n,x) { int sum=0; for1(0,m-1,i) { inc(sum,1ll*js[i+x]*ni[x]%mod*ni[i]%mod*qpow(b,i)%mod*(qpow(h,m)-qpow(h,i)+mod)%mod*qpow(h-1,mod-2)%mod); } //s[x]=sum; cout<<sum<<" "<<s[x]<<endl; } */ int k=0,ans=0; int p1=1,p2=1,p3=qpow(h,m); for1(1,n,i) { inc(k,1ll*p1*s[i-1]%mod); inc(ans,1ll*k*p2%mod); p1=1ll*a*p1%mod; p2=1ll*p2*p3%mod; } return 1ll*c*ans%mod; } int main () { //freopen("a.in","r",stdin); read(); n=read(),m=read(),h=read(),mod=read(),a=read(),b=read(),c=read(); for1(1,n,i) f[i]=read(); js[0]=1; for1(1,M-5,i) js[i]=1ll*i*js[i-1]%mod; ni[M-5]=qpow(js[M-5],mod-2); FOR2(M-5,1,i) ni[i-1]=1ll*i*ni[i]%mod; for1(1,M-5,i) inv[i]=1ll*js[i-1]*ni[i]%mod; cout<<(cal1()+cal2())%mod<<endl; }
Round 3(11.6)
T1:
就是个爆搜$border$。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; typedef unsigned long long unll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=1e2+5; const int N=1e4+5; const int mod=1e9+7; int n,m,K; int size,dis[N],nxt[N][M],f[M][N]; map <unll,int> vis; inline int qpow(int x,int k) { int sum=1; for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) sum=1ll*x*sum%mod; return sum; } inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} struct node { int be[M]; inline void init() { for1(1,m,i) be[i]=i; } inline int find(int x) { if(x==be[x]) return x; return be[x]=find(be[x]); } inline void add(int x) { for1(1,x,i) be[find(i)]=find(m-x+i); } inline int count() { int ans=0; for1(1,m,i) if(be[i]==i) ++ans; return ans; } inline unll get_hash() { unll x=0; for1(1,m,i) x=x*mod+find(i); return x; } }; vector <node> sta; inline void add_(node x,int way_) { unll t=x.get_hash(); vis[t]=++size; dis[size]=way_; sta.push_back(x); } inline int calc(int x) { m=x; size=0; sta.clear(); vis.clear(); memset(f,0,sizeof(f)); memset(dis,0,sizeof(dis)); memset(nxt,0,sizeof(nxt)); node t; t.init(); add_(t,1); for1(1,size,i) { t=sta[i-1]; node to; FOR2(m-1,1,j) { if(dis[i]+m-j>n-m+1) continue; to=t,to.add(j); unll num=to.get_hash(); if(!vis.count(num)) add_(to,dis[i]+m-j); nxt[i][j]=vis[num]; dis[nxt[i][j]]=min(dis[nxt[i][j]],dis[i]+m-j); } } f[1][1]=1; for1(1,n-m+1,i) { for1(1,size,j) if(f[i][j]) { for1(i+1,n-m+1,k) { int sel=f[i][j]; if(i+m-k>=1) inc(f[k][nxt[j][i+m-k]],mod-sel); else inc(f[k][j],mod-1ll*sel*qpow(K,k-i-m)%mod); } } } int ans=0; for1(1,size,j) { int num=sta[j-1].count(); for1(2,n-m+1,i) inc(ans,1ll*f[i][j]*qpow(K,n-m+1-i+num)%mod); } return mod-ans; } int main () { //freopen("a.in","r",stdin); n=read(),K=read(); int ans=0; for1(1,n-1,i) inc(ans,calc(i)); cout<<ans<<endl; }
T2:
写不动啊。
T3:
按照标题 "基础找规律练习题“ 搞就行了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i) #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i) using namespace std; typedef long long ll; #define M 1000005 int n,f[M]; struct node {int x,y;}p[M]; int main () { //freopen("a.in","r",stdin); cin>>n; f[1]=3; for1(1,n,i) f[i+1]=f[i]+2+(i&1); int ans=0; for1(1,n,i) { if(f[i]>n) break; ans=i; int t=f[i]-2*i; if(f[i]-f[i-1]==3) p[i]=(node){t,2*i}; else p[i]=(node){1,2*i}; } //cout<<p[1].x<<" "<<p[1].y<<endl; p[1].y+=ans/2; for(int i=2,las=ans/2-1;las>0;--las,i+=2) { p[i].x+=las,p[i].y+=las; p[i+1].y+=las; } for(int i=ans,cnt=0;i;cnt+=f[i]-f[i-1]==2,--i) { p[i].x+=cnt,p[i].y+=cnt; } printf("%d ",ans); for1(1,ans,i) printf("%d %d ",p[i].x,p[i].y); }
Round 4(11.14):
T1:
就是一个简单的轮廓线dp。
我判状态是否存在用的是方案数,结果$mod998244353$出0了。
令我很震惊,因为这个错误卡了半天。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i) #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=2e3+5; const int mod=998244353; int n,m; char a[10][M]; int g[M],js[M],inv[M]; inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} struct point { int x,y; }; struct node { int cnt; int f[M][M]; bool vis[M][M]; point zt[M*M]; inline void add_(int x,int y,int z) { if(!vis[x][y]) vis[x][y]=1,zt[++cnt]=(point){x,y}; inc(f[x][y],z); } inline void clear() { for1(1,cnt,i) vis[zt[i].x][zt[i].y]=f[zt[i].x][zt[i].y]=0; cnt=0; } }Y[2]; inline bool check(int x) { for1(1,n,i) if(a[i][m]=='*'&&!(x>>i-1&1)) return 0; return 1; } inline int qpow(int x,int ci) { int sum=1; for(;ci;ci>>=1,x=1ll*x*x%mod) if(ci&1) sum=1ll*x*sum%mod; return sum; } inline int Com(int x,int y) { if(x<y) return 0; return 1ll*js[x]*inv[y]%mod*inv[x-y]%mod; } int main () { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); n=read(),m=read(); for1(1,n,i) scanf("%s",a[i]+1); int ha=0; for1(1,n,i) for1(1,m,j) ha+=a[i][j]=='*'; if(!ha) return puts("0"),0; int opt=0; Y[0].add_(0,0,1); //j状态,k个数 for1(1,m,i) { for1(1,n,j) { Y[opt^1].clear(); for1(1,Y[opt].cnt,k) { point x=Y[opt].zt[k]; int sel=Y[opt].f[x.x][x.y]; if(!(x.x>>j-1&1)&&a[j][i-1]=='*') { Y[opt^1].add_(x.x|(1<<j-1),x.y+1,sel); if(j!=1) Y[opt^1].add_(x.x|(1<<j-1)|(1<<j-2),x.y+2,sel); } else { Y[opt^1].add_(x.x-((x.x>>j-1&1)<<j-1),x.y,sel); if(i!=1) Y[opt^1].add_(x.x|(1<<j-1),x.y+1,sel); if(j!=1) Y[opt^1].add_(x.x|(1<<j-1)|(1<<j-2),x.y+1,sel); if(i!=1&&j!=1) Y[opt^1].add_(x.x|(1<<j-1)|(1<<j-2),x.y+2,sel); } } opt^=1; } } int ans=0; int tot=n*(m-1)+m*(n-1); js[0]=1; for1(1,tot,i) js[i]=1ll*i*js[i-1]%mod; inv[tot]=qpow(js[tot],mod-2); FOR2(tot,1,i) inv[i-1]=1ll*i*inv[i]%mod; for1(1,Y[opt].cnt,i) { point x=Y[opt].zt[i]; if(check(x.x)) inc(g[x.y],Y[opt].f[x.x][x.y]); } for1(1,tot,i) g[i]=1ll*g[i]*js[i]%mod; for1(1,tot,i) for1(i+1,tot,j) inc(g[j],mod-1ll*g[i]*Com(tot-i,j-i)%mod*js[j-i]%mod); for(int i=1,sum=0;i<=tot;++i) { inc(sum,1ll*tot*qpow(tot-i+1,mod-2)%mod); inc(ans,1ll*sum*g[i]%mod*inv[tot]%mod*js[tot-i]%mod); } cout<<ans<<endl; }
T3:
非常巧妙的题,给出题人点赞。
首先我们考虑在链上怎么做。
设$f(n,m)$代表$n$个点分成$m$组,每段的乘积和。
然后就可以列出一个容斥的式子:$(sum_{i=1}^{n}c_{i})!prod_{i=1}^{n} f(a_{i},b_{i})*(-1)^{b_{i}-c{i}}*inom{b_{i}-1}{c_{i}-1}*frac{1}{c_{i}!}$。
我一开始傻了,一直以为只有$f$才是乘的关系,想了半天也不知道怎么优化。
其实这个式子还是非常容易优化到$O(m*log^2n)$的。
一个瓶颈就是$f(n,m)$,其实打表还是很容易发现$f(n,m)=inom{n+m-1}{2*m-1}$的。
证明的话就很巧妙,我们观察其实这是一个类似挡板法的式子。
只有上面加了个$m$,其实就是选出$2*m-1$个点,偶数的当挡板,奇数的当$0,1,2...$,这样就是乘的关系了!
然后就是环了。
其实我们考虑可以算出以1开头,并且不以1结尾的方案数。
这个还是很好算的,直接用以1开头方案数减去以1开头且以1结尾方案数就行了。
但是还要考虑循环同构的问题。
如果循环节为T,每一个点算的次数就是$T*frac{b}{T}$,所以除以$b$就行了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=2e5+5; const int N=1e6+5; const int max_len=1<<19; const int mod=998244353; int n; int a[M],A[N],B[N]; int fac[N],inv[N],G[2][N]; vector <int> g[M]; inline int qpow(int x,int k) { int t=1; for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) t=1ll*t*x%mod; return t; } inline int com(int x,int y) { if(x<y) return 0; return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod; } inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline int get_len(int x) { int len=1; for(;len<=2*x;len<<=1); return len; } inline void ntt(int *a,int len,int H) { for(int i=0,j=0;i<len;++i) { if(i>j) swap(a[i],a[j]); for(int t=len>>1;(j^=t)<t;t>>=1); } int u,t; for(int m=2;m<=len;m<<=1) { int p=m>>1; for(int i=0;i<p;++i) G[1][i]=G[0][H==1?max_len/m*i:max_len-max_len/m*i]; for(int i=0;i<len;i+=m) for(int j=0;j<p;++j) { u=a[i+j]; t=1ll*a[i+j+p]*G[1][j]%mod; a[i+j]=(u+t)%mod; a[i+j+p]=(u-t+mod)%mod; } } if(H==-1) { int inv_=qpow(len,mod-2); for1(0,len-1,i) a[i]=1ll*inv_*a[i]%mod; } } inline vector<int> mul(vector <int> ls,vector <int> rs) { //cout<<ls.size()<<" "<<rs.size()<<endl; vector <int> cc; int len=get_len(max(ls.size(),rs.size())); fill(A,A+len,0),fill(B,B+len,0); FOR2(ls.size(),1,i) A[i-1]=ls[i-1]; FOR2(rs.size(),1,i) B[i-1]=rs[i-1]; ntt(A,len,1),ntt(B,len,1); for1(0,len-1,i) A[i]=1ll*A[i]*B[i]%mod; ntt(A,len,-1); int size=ls.size()+rs.size(); for1(0,size,i) cc.push_back(A[i]); return cc; } inline vector<int>solve (int l,int r) { if(l==r) { vector <int> cc; cc.push_back(0); for1(1,a[l],i) cc.push_back(g[l][i]); return cc; } int mid=l+r>>1; return mul(solve(l,mid),solve(mid+1,r)); } signed main () { //freopen("a.in","r",stdin); //freopen("b.out","w",stdout); n=read(); for1(1,n,i) a[i]=read(); sort(a+1,a+n+1); reverse(a+1,a+n+1); while (!a[n]&&n) --n; if(n==1) return cout<<a[1]<<endl,0; fac[0]=1; for1(1,N-5,i) fac[i]=1ll*i*fac[i-1]%mod; inv[N-5]=qpow(fac[N-5],mod-2); FOR2(N-5,1,i) inv[i-1]=1ll*i*inv[i]%mod; G[0][0]=1; for(int i=1,x=qpow(3,(mod-1)/max_len);i<=max_len;++i) G[0][i]=1ll*x*G[0][i-1]%mod; //666 for1(1,n,i) { g[i].resize(a[i]+10); int len=get_len(a[i]+1); fill(A,A+len+1,0); fill(B,B+len+1,0); for1(1,a[i],j) A[j]=1ll*fac[j-1]*fac[a[i]+j-1]%mod*inv[2*j-1]%mod*inv[a[i]-j]%mod; for1(1,a[i],j) if(j&1) A[j]=mod-A[j]; for1(1,a[i],j) B[j]=inv[a[i]-j]; ntt(A,len,1),ntt(B,len,1); for1(0,len-1,j) A[j]=1ll*A[j]*B[j]%mod; ntt(A,len,-1); for1(1,a[i],j) g[i][j]=1ll*inv[j]*inv[j-1]%mod*A[j+a[i]]%mod; for1(1,a[i],j) if(j&1) g[i][j]=mod-g[i][j]; } vector <int> cc=solve(2,n); //cout<<"-_-"<<endl; int ans=0,sum=0; for1(2,n,i) sum+=a[i]; for1(1,1,i) { int len=get_len(a[i]+1); fill(A,A+len+1,0); fill(B,B+len+1,0); for1(1,a[i],j) A[j]=1ll*fac[j-1]*fac[a[i]+j-1]%mod*inv[2*j-1]%mod*inv[a[i]-j]%mod; for1(1,a[i],j) if(j&1) A[j]=mod-A[j]; for1(1,a[i],j) A[j]=1ll*A[j]*inv[j]%mod*fac[j-1]%mod; for1(1,a[i],j) B[j]=inv[a[i]-j]; ntt(A,len,1),ntt(B,len,1); for1(0,len-1,j) A[j]=1ll*A[j]*B[j]%mod; ntt(A,len,-1); for1(1,a[i],j) g[i][j]=1ll*inv[j-1]*inv[j-1]%mod*A[j+a[i]]%mod; for1(1,a[i],j) if(j&1) g[i][j]=mod-g[i][j]; } int len_=get_len(max(sum+1,a[1]+1)); fill(A,A+len_,0),fill(B,B+len_,0); for1(0,sum,i) A[i]=cc[i]; for1(1,a[1],i) B[i]=g[1][i]; ntt(A,len_,1),ntt(B,len_,1); for1(0,len_-1,i) A[i]=1ll*A[i]*B[i]%mod; ntt(A,len_,-1); for1(1,sum+a[1],i) inc(ans,1ll*A[i]*fac[i-1]%mod); g[1][1]=0; for1(2,a[1],j) g[1][j]=1ll*(j-1)*g[1][j]%mod; fill(A,A+len_,0),fill(B,B+len_,0); for1(0,sum,i) A[i]=cc[i]; for1(1,a[1],i) B[i]=g[1][i]; ntt(A,len_,1),ntt(B,len_,1); for1(0,len_-1,i) A[i]=1ll*A[i]*B[i]%mod; ntt(A,len_,-1); for1(2,sum+a[1],i) inc(ans,mod-1ll*A[i]*fac[i-2]%mod); cout<<1ll*ans*(sum+a[1])%mod<<endl; }
Round 5(11.15)
T1:
T2:
显然是每个人选一个,然后另一个人选一段然后再选一个。
你会发现你的所有思路都是正着的,然后他改变最后一个的询问就很难受。
然后我们考虑倒推,因为最后一只如果$>1e6$直接选最后一个,所以$sum$还是$1e6$级别的。
令$f(i,j)$代表到
Round 7(11.18)
我和zyd,yht的比赛,整体简单了,不过还是不错的。
T1:
我出的题,就是一个简单的贪心。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 200005 int n,t,s,a[M]; pair <int,int> cc[M]; inline ll calc_() { if(t<s) { vector <int> sta; for1(1,t,i) sta.push_back(s-i); if(t) { //如果没有你就re了 sta.pop_back(); sta.push_back(1); } for1(2,s-t,i) sta.push_back(i); for1(s+1,n,i) sta.push_back(i); int x=sta[0]; ll ans=a[max(s,x)]-a[min(s,x)]; for1(1,n-2,i) { int x=sta[i]; int y=sta[i-1]; ans+=a[max(x,y)]-a[min(x,y)]; } return ans; } multiset <int> q; int cnt=0; for1(s+1,n-1,i) cc[++cnt]=make_pair(a[i+1]-a[i],i); sort(cc+1,cc+cnt+1); cnt=t-s+1; ll Min=0,tot=0; for1(1,cnt,i) tot+=cc[i].first,q.insert(cc[i].first); Min=tot*2; FOR2(n-1,n-cnt,j) { multiset<int>::iterator it=q.lower_bound(a[j+1]-a[j]); if(it==q.end()) it--; tot-=*it; q.erase(it); Min=min(Min,a[n]-a[j]+tot*2); } return Min+2*(a[s]-a[1])+(a[n]-a[s]); } int main() { //freopen("a.in","r",stdin); //freopen("zj.out","w",stdout); n=read(),t=read(),s=read(); for1(1,n,i) a[i]=read(); if(s!=1&&!t) return puts("-1"),0; if(s!=n&&!(n-t-1)) return puts("-1"),0; ll ans=calc_(); for1(1,n,i) a[i]=-a[i]; for1(1,n/2,i) swap(a[i],a[n-i+1]); t=n-1-t,s=n+1-s; ans=min(ans,calc_()); printf("%lld ",ans); }
T2:
审题的时候讨论了一发,结果忘了怎么做了。
今天差一点想自闭,还好最后想出来了。
不满足条件的式子:
$suma+sum_{i=1}^{index-1}a_{pos_{i}}<sum_{i=1}^{index}b_{pos_{i}}$。
然后我们考虑不合法只可能是a对b或b对a,不可能两边互相不合法。(因为我们先弄完自己的题才可以)
然后我们就考虑dp出来a对b(b对a)不合法的方案数。
正难则反,我们考虑dp出来合法的用$2^n$减去。
设$f(i,j,k)$代表第i个点选了b,$[1,i]$中选b的和为j的方案数,$[i+1,n]$的选a的和为k的方案数。
为啥k是$[i+1,n]$呢,因为我们枚举下一个u转移的时候$[1,u-1]$都会加到a上,就不会受到a,b数组不同的干扰。
然后我们就可以枚举初始的k转移了,显然这时发现所有的状态都直接初始化掉就行了。
复杂度$O(n^3*w^2)$。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=50; const int N=1e3; int n; int a[M],b[M],sum[2][M]; ll f[M][N][N]; inline ll calc_() { for1(1,n,i) { sum[0][i]=sum[0][i-1]+a[i]; sum[1][i]=sum[1][i-1]+b[i]; } ll ans=0; memset(f,0,sizeof(f)); for1(0,sum[0][n],i) f[0][0][i]=1; for1(0,n,i) { for1(0,sum[1][i],j) for1(0,sum[0][n]-sum[0][i],k) { if(!f[i][j][k]) continue; int t=0; ll sel=f[i][j][k]; ans+=(sum[0][n]-sum[0][i]==k)*sel; for1(i+1,n,u) { if(t>k) break; if(k-t+sum[0][u-1]>=j+b[u]) f[u][j+b[u]][k-t]+=sel; t+=a[u]; } } } return (1ll<<n)-ans; } int main() { //freopen("a.in","r",stdin); n=read(); for1(1,n,i) a[i]=read(); for1(1,n,i) b[i]=read(); ll ans=1ll<<n; ans-=calc_(); for1(1,n,i) swap(a[i],b[i]); ans-=calc_(); cout<<ans<<endl; }
T3:
59分很显然,直接$O(n^4*log_{k}{n})$的dp+矩乘就行了。
感觉满分算法就需要一点灵活了。
以为显然这个题没啥别的做法了,而且现在的状态已经是最简了。
然后把$f(i,j)$和$f(i,j+1)$打表出来发现都是满足一个变化规律。
令这个变化规律为$G(A)$。
则:$G(prod_{j=0}^{k-1} A_{i-1,j})=prod_{j=0}^{k-1}G(A_{i-1,j})$
即:$G(A*B)=G(A)*G(B)$
然后就可以倍增了,$O(n^3*log_{2}{n})$。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=205; const int mod=998244353; ll n; int K,len,a[M]; pair <int,int> to[M][M][M]; struct matrix { int a[M][M]; inline void clear() { memset(a,0,sizeof(a)); } inline void out() { for1(1,K+1,i) { for1(1,K+1,j) cout<<a[i][j]<<" "; cout<<endl; } cout<<endl; } inline void init(int x) { for1(1,K+1,i) a[i][i]=1; for1(1,K+1,i) a[i][x]=1; } inline void turn_(int x) { matrix t; t.clear(); for1(1,K+1,i) for1(1,K+1,j) { pair<int,int> p=to[x][i][j]; t.a[i][j]=a[p.first][p.second]; } for1(1,K+1,i) for1(1,K+1,j) a[i][j]=t.a[i][j]; } }f[100]; inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline matrix operator *(const matrix &a,const matrix &b) { matrix t; t.clear(); for1(1,K+1,i) for1(1,K+1,j) if(a.a[i][j]) for1(1,K+1,k) if(b.a[j][k]) inc(t.a[i][k],1ll*a.a[i][j]*b.a[j][k]%mod); return t; } inline matrix operator ^(matrix a,int x) { matrix ans; ans.clear(); for1(1,K+1,i) ans.a[i][i]=1; for1(0,7,i) { if(x>>i&1) { ans=ans*a; a.turn_(1<<i); } matrix t=a; a.turn_(1<<i); a=t*a; } return ans; } signed main () { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); cin>>n>>K; --n; ll x=n; while (x) { a[++len]=x%K; x/=K; } len=max(1,len); for1(1,K+1,i) for1(1,K+1,j) to[0][i][j]=make_pair(i,j); for1(1,K,i) { for1(1,K,x) for1(1,K,y) { to[i][x%K+1][y%K+1]=to[i-1][x][y]; } for1(1,K,x) to[i][K+1][x%K+1]=to[i-1][K+1][x]; for1(1,K+1,x) to[i][x][K+1]=to[i-1][x][K+1]; } f[1].init(1); for1(2,len,i) f[i]=f[i-1]^K; int sum=0; matrix ans; ans.clear(); for1(1,K+1,i) ans.a[i][i]=1; for(int i=len;i>=1;sum=(sum+a[i--])%K) { f[i].turn_(sum); ans=ans*(f[i]^(a[i]+(i==1))); } int tot=0; for1(1,K+1,i) inc(tot,ans.a[K+1][i]); cout<<tot<<endl; }
Round 9(11.23)
T3:
感觉如果不知道这个点真的比较难想出来了。
就是一个关于循环子串的性质。
设$g(i)$为以$i$为结尾的循环子串不同的循环节长度个数和。
则:$sum_{i=1}^{n}g(i)$是$n*logn$级别的。
证明我还没证出来......
知道这个之后就直接根据之前的容斥dp搞就行了。
得到这些东西就枚举循环节长度,利用如果$T>1$一定会覆盖$>1$个相邻点来搞就行了。
复杂度$O(n*log^2n)$。
话说后缀自动机在uoj上过不了真的是悲伤。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=2e5+5; const int N=5e5+5; const int mod=998244353; int n; char s[M]; int f[M],w[N*10],g[N*10][2]; map <int,bool> vis[M]; vector <int> cc[M]; inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} struct suffix_automatic { char a[M]; int las,num,Lg[N*2]; int mx[N],fa[N],nxt[N][26]; int id[N],st[N],pos[M],fir[N],rmq[N*2][20]; vector <int> to[N]; inline void extend_(int c) { int p=las,np=las=++num; mx[np]=mx[p]+1; while (p&&!nxt[p][c]) nxt[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=nxt[p][c]; if(mx[q]==mx[p]+1) fa[np]=q; else { int nq=++num; mx[nq]=mx[p]+1; memcpy(nxt[nq],nxt[q],sizeof(nxt[nq])); fa[nq]=fa[q],fa[q]=fa[np]=nq; while (nxt[p][c]==q) nxt[p][c]=nq,p=fa[p]; } } } inline void dfs(int x) { id[x]=++id[0]; st[id[0]]=x; rmq[++rmq[0][0]][0]=id[x]; fir[x]=rmq[0][0]; for(auto v:to[x]) { dfs(v); rmq[++rmq[0][0]][0]=id[x]; } } void init() { las=num=1; for1(1,n,i) extend_(a[i]-'a'),pos[i]=las; for1(2,num,i) to[fa[i]].push_back(i); dfs(1); for1(2,rmq[0][0],i) Lg[i]=Lg[i>>1]+1; for1(1,19,i) FOR2(rmq[0][0]-(1<<i-1),1,j) rmq[j][i]=min(rmq[j][i-1],rmq[j+(1<<i-1)][i-1]); } inline int query(int x,int y) { x=pos[x],y=pos[y]; int l=fir[x],r=fir[y]; if(l>r) swap(l,r); return mx[st[min(rmq[l][Lg[r-l+1]],rmq[r-(1<<Lg[r-l+1])+1][Lg[r-l+1]])]]; } }a,b; int main() { //freopen("a.in","r",stdin); scanf("%s",s+1); n=strlen(s+1); for1(1,n,i) a.a[i]=b.a[n-i+1]=s[i]; a.init(),b.init(); int cnt=0; for1(1,n/2,len) { for(int i=len;i+len<=n;i+=len) { int l=a.query(i,i+len); int r=b.query(n-i,n-i-len); if(l+r<len) continue; l=i+1-l,r=i+len+r; if(vis[l].count(r)) continue; vis[l][r]=1; int Max=min(r,l+3*len-2); for(int j=l+2*len-1;j<=Max;++j) { w[++cnt]=len; for(int k=j;k<=r;k+=len) cc[k].push_back(cnt); } } } f[0]=1; int sum=1; for1(1,n,i) { f[i]=sum; for(auto j:cc[i]) { int kth=i/w[j]; inc(g[j][kth&1],f[i-2*w[j]]); inc(f[i],mod-2ll*g[j][kth&1]%mod); } inc(sum,f[i]); } cout<<f[n]<<endl; }
11.25:
T1:
推一推会发现可以分块+单调栈。
数据太水,随便写都能过,我也不知道我写的代码对不对。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=1e5+5; const int N=5e2+5; #define ff(x,y,z) (mx[x][y-(x-1)*m]*z+f[y-1]) int n,m; ll f[M],sum[M]; ll L[M],R[M],mn[M/N+10][N]; int id[M],be[M],mx[M/N+10][N]; struct node { int x,y,z; //l,t,w; inline void in() { x=read(),y=read(),z=read(); } }p[M]; vector <node> a[M/N+10][N]; inline ll get_(int x,int Max,ll k) { ll ans=1e18; int l=(x-1)*m+1,r=min(n,x*m),mid,pos=r+1; while (l<=r) { mid=l+r>>1; if(mx[x][mid-(x-1)*m]<=Max) pos=mid,r=mid-1; else l=mid+1; } if(pos<=min(n,x*m)) ans=Max*k+mn[x][pos-(x-1)*m]; l=1,r=a[x][pos-(x-1)*m-1].size(); while (l<=r) { mid=l+r>>1; node t=a[x][pos-(x-1)*m-1][mid-1]; if(t.y<k) l=mid+1; else if(k<t.x) r=mid-1; else return min(ans,ff(x,t.z,k)); } return ans; } inline ll get__(int x,int Max,ll k) { ll ans=1e18; int l=(x-1)*m+1,r=min(n,x*m),mid,pos=r+1; while (l<=r) { mid=l+r>>1; if(mx[x][mid-(x-1)*m]<=Max) pos=mid,r=mid-1; else l=mid+1; } if(pos<=min(n,x*m)) ans=Max*k+mn[x][pos-(x-1)*m]; l=1,r=a[x][pos-(x-1)*m-1].size(); while (l<=r) { mid=l+r>>1; node t=a[x][pos-(x-1)*m-1][mid-1]; if(t.y<k) l=mid+1; else if(k<t.x) r=mid-1; else return min(ans,ff(x,t.z,k)); } return ans; } inline void build(int x) { int l=(x-1)*m+1; int r=min(n,x*m); mn[x][r-l+2]=1e18; FOR2(r,l,i) { mx[x][i-l+1]=max(mx[x][i-l+2],p[i].y); mn[x][i-l+1]=min(mn[x][i-l+2],f[i-1]); } int size=0; for1(l,r,i) { if(size&&ff(x,i,sum[n])>=ff(x,id[size],sum[n])) { for1(1,size,j) a[x][i-l+1].push_back((node){L[j],R[j],id[j]}); continue; } while (size) { if(ff(x,i,L[size])>ff(x,id[size],L[size])) break; if(ff(x,i,R[size])>ff(x,id[size],R[size])) break; --size; } ++size; id[size]=i; if(size==1) L[size]=0,R[size]=sum[n]; else { R[size]=sum[n]; L[size]=(f[id[size-1]]-f[i-1])/(mx[x][i-l+1]-mx[x][id[size-1]-l+1]); R[size-1]=L[size]-1; } for1(1,size,j) a[x][i-l+1].push_back((node){L[j],R[j],id[j]}); } } int main () { //freopen("a.in","r",stdin); n=read(),m=N-5; for1(1,n,i) p[i].in(),sum[i]=sum[i-1]+p[i].z; for1(1,n,i) be[i]=(i-1)/m+1; for1(1,n,i) { int Max=0; int pos=p[i].x+1; f[i]=1e18; if(be[pos]==be[i]) { FOR2(i,pos,j) { Max=max(Max,p[j].y); f[i]=min(f[i],f[j-1]+(sum[n]-sum[i])*Max); } } else { FOR2(i,(be[i]-1)*m+1,j) { Max=max(Max,p[j].y); f[i]=min(f[i],f[j-1]+(sum[n]-sum[i])*Max); } FOR2(be[i]-1,be[pos]+1,j) { f[i]=min(f[i],get_(j,Max,sum[n]-sum[i])); Max=max(Max,mx[j][1]); } FOR2(be[pos]*m,pos,j) { Max=max(Max,p[j].y); f[i]=min(f[i],f[j-1]+(sum[n]-sum[i])*Max); } } if(be[i+1]!=be[i]) build(be[i]); } cout<<f[n]<<endl; }
T2:
很好的题,给出题人点赞。
最重要的一个idea就是从一个点走到叶子的路径上不同权值的段数是$log$的。
我太傻了没有想到。
所以自然而然地我们就会想出来一个线段树维护区间&,在给定线段树上二分的思路。
不过这是$log^3n$的,并过不去。
这是我们发现向一个方向的儿子走有一个端点是不变的,所以直接在线段树上二分就行了。
$O(n*log^2n)$
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=1e6+5; const int inf=(1<<30)-1; int n,m; int root,size; struct point{int g,l,r;}; struct node { int l,r; int ch[2]; }p[M*2]; int dep[M*2]; unordered_map <int,int> st[M]; vector <int> a[M],b[M]; struct Segment{ #define ls (g<<1) #define rs (g<<1|1) bool vis[M*4]; vector <point> sta; int rev[M*4],data[M*4][2]; inline void push_up(int g) { data[g][0]=data[ls][0]&data[rs][0]; data[g][1]=data[ls][1]&data[rs][1]; } inline void push_down(int g) { int x=ls; vis[x]=1; int t[2]={data[x][0]&rev[g],data[x][1]&rev[g]}; data[x][0]+=t[1]-t[0],data[x][1]+=t[0]-t[1]; data[x][0]|=data[g][0]; data[x][1]|=data[g][1]; data[x][0]-=data[g][1]&data[x][0]; data[x][1]-=data[g][0]&data[x][1]; rev[x]^=rev[g]^(t[0]|t[1]); x=rs; vis[x]=1; t[0]=data[x][0]&rev[g],t[1]=data[x][1]&rev[g]; data[x][0]+=t[1]-t[0],data[x][1]+=t[0]-t[1]; data[x][0]|=data[g][0]; data[x][1]|=data[g][1]; data[x][0]-=data[g][1]&data[x][0]; data[x][1]-=data[g][0]&data[x][1]; rev[x]^=rev[g]^(t[0]|t[1]); vis[g]=rev[g]=0; } inline void push_(int g,int opt,int x) { vis[g]=1; if(opt==1) { // and data[g][0]|=inf^x; data[g][1]-=data[g][0]&data[g][1]; } else if(opt==2) { // or data[g][1]|=x; data[g][0]-=data[g][0]&x; } else { // xor int t[2]={data[g][0]&x,data[g][1]&x}; data[g][0]+=t[1]-t[0]; data[g][1]+=t[0]-t[1]; rev[g]^=x^(t[0]|t[1]); } } inline void init(int g,int l,int r) { if(l==r) { int x=read(); data[g][1]=x; data[g][0]=x^inf; return; } int mid=l+r>>1; init(ls,l,mid); init(rs,mid+1,r); push_up(g); } inline void T_add(int g,int l,int r,int opt,int x,int lx,int rx) { if(lx<=l&&rx>=r) return push_(g,opt,x),void(); if(vis[g]) push_down(g); int mid=l+r>>1; if(lx<=mid) T_add(ls,l,mid,opt,x,lx,rx); if(rx>mid) T_add(rs,mid+1,r,opt,x,lx,rx); push_up(g); } inline int query(int g,int l,int r,int lx,int rx) { if(lx<=l&&rx>=r) return data[g][1]; if(vis[g]) push_down(g); int mid=l+r>>1; if(rx<=mid) return query(ls,l,mid,lx,rx); else if(lx>mid) return query(rs,mid+1,r,lx,rx); else return query(ls,l,mid,lx,rx)&query(rs,mid+1,r,lx,rx); } inline void query_seg(int g,int l,int r,int lx,int rx) { if(lx<=l&&rx>=r) return sta.push_back((point){g,l,r}),void(); if(vis[g]) push_down(g); int mid=l+r>>1; if(lx<=mid) query_seg(ls,l,mid,lx,rx); if(rx>mid) query_seg(rs,mid+1,r,lx,rx); } inline int query_left(int g,int l,int r,int now,int to) { while (l!=r) { if(vis[g]) push_down(g); int mid=l+r>>1; if((now&data[ls][1])<=to) g=ls,r=mid; else now&=data[ls][1],g=rs,l=mid+1; } return l; } inline int query_right(int g,int l,int r,int now,int to) { while (l!=r) { if(vis[g]) push_down(g); int mid=l+r>>1; if((now&data[rs][1])<=to) g=rs,l=mid+1; else now&=data[rs][1],g=ls,r=mid; } return l; } inline int get_leftson(int x,int las) { sta.clear(); query_seg(1,1,n,p[x].l,n); int pos,num=inf,tot=sta.size(); for1(1,tot,i) { point t=sta[i-1]; if((num&data[t.g][1])<=las) { pos=query_left(t.g,t.l,t.r,num,las); break; } num&=data[t.g][1]; } int l=1,r=a[p[x].l].size(),mid,ans; while (l<=r) { int mid=l+r>>1; int id=a[p[x].l][mid-1]; if(p[id].r>=pos) ans=id,l=mid+1; else r=mid-1; } return ans; } inline int get_rightson(int x,int las) { sta.clear(); query_seg(1,1,n,1,p[x].r); int pos,num=inf,tot=sta.size(); FOR2(tot,1,i) { point t=sta[i-1]; if((num&data[t.g][1])<=las) { pos=query_right(t.g,t.l,t.r,num,las); break; } num&=data[t.g][1]; } int l=1,r=b[p[x].r].size(),mid,ans; while (l<=r) { int mid=l+r>>1; int id=b[p[x].r][mid-1]; if(p[id].l<=pos) ans=id,l=mid+1; else r=mid-1; } return ans; } #undef ls #undef rs }Y; void build(int &g,int l,int r,int depth) { g=++size; st[l][r]=g; dep[g]=depth; p[g].l=l,p[g].r=r; a[l].push_back(g),b[r].push_back(g); if(l==r) return; int mid=read(); build(p[g].ch[0],l,mid,depth+1); build(p[g].ch[1],mid+1,r,depth+1); } signed main () { //freopen("a.in","r",stdin); //freopen("b.out","w",stdout); n=read(),m=read(); Y.init(1,1,n); build(root,1,n,0); while (m--) { int opt=read(); int x,l=read(),r=read(); if(opt<=3) { x=read(); Y.T_add(1,1,n,opt,x,l,r); } else { ll ans=0; int las,x=st[l][r]; las=Y.query(1,1,n,p[x].l,p[x].r); opt=__builtin_parity(las); while (1) { int y=opt?Y.get_leftson(x,las):Y.get_rightson(x,las); ans+=1ll*las*(dep[y]-dep[x]+1); if(p[y].l==p[y].r) break; x=p[y].ch[!opt]; las=Y.query(1,1,n,p[x].l,p[x].r); opt=__builtin_parity(las); } printf("%lld ",ans); } } }
12.10:
T1:
直接分块$O(n*sqrt(n*logn))$就好了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; typedef unsigned long long unll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=1e5+5; const int len=300; int n,m,opt; int a[M],b[M]; int e_size,head[M]; int id[M],mx[M],fa[M],top[M],size[M],son[M]; int be[M],data[M/len+5],ans[M/len+5],sta[M/len+5][M*2]; struct node { int v,nxt; }e[M*2]; inline void e_add(int u,int v) { e[++e_size]=(node){v,head[u]}; head[u]=e_size; } inline void dfs1(int x) { size[x]=1; for(int i=head[x];i;i=e[i].nxt) { int v=e[i].v; if(v==fa[x]) continue; fa[v]=x; dfs1(v); size[x]+=size[v]; if(size[v]>size[son[x]]) son[x]=v; } } inline void dfs2(int x,int tp) { top[x]=tp; id[x]=++id[0]; if(son[x]) dfs2(son[x],tp); for(int i=head[x];i;i=e[i].nxt) { int v=e[i].v; if(v==fa[x]||v==son[x]) continue; dfs2(v,v); } mx[x]=id[0]; } inline void add_(int x,int w) { if(b[x]+data[be[x]]>0) --ans[be[x]]; if(b[x]+data[be[x]]+w>0) ++ans[be[x]]; if(max(b[x],-b[x])<=M) --sta[be[x]][b[x]+M]; b[x]+=w; if(max(b[x],-b[x])<=M) ++sta[be[x]][b[x]+M]; } inline void T_add(int l,int r,int w) { if(be[l]==be[r]) { for1(l,r,i) add_(i,w); return; } else { for1(l,be[l]*len,i) add_(i,w); for1(be[l]+1,be[r]-1,i) { if(w==1) { ans[i]+=sta[i][M-data[i]]; ++data[i]; } else { --data[i]; ans[i]-=sta[i][M-data[i]]; } } for1((be[r]-1)*len+1,r,i) add_(i,w); } } inline int query(int l,int r) { int tot=0; if(be[l]==be[r]) { for1(l,r,i) tot+=b[i]+data[be[i]]>0; } else { for1(l,be[l]*len,i) tot+=b[i]+data[be[i]]>0; for1(be[l]+1,be[r]-1,i) tot+=ans[i]; for1((be[r]-1)*len+1,r,i) tot+=b[i]+data[be[i]]>0; } return tot; } int main () { //freopen("a.in","r",stdin); n=read(),m=read(),opt=read(); for1(2,n,i) { int x=read(),y=read(); e_add(x,y),e_add(y,x); } for1(1,n,i) a[i]=read(); dfs1(1); dfs2(1,1); for1(1,n,i) be[i]=(i-1)/len+1; for1(1,n,i) b[id[i]]=a[i]; for1(1,n,i) if(max(-b[i],b[i])<=M) ++sta[be[i]][b[i]+M]; for1(1,n,i) if(b[i]>0) ++ans[be[i]]; int las_ans=0; while (m--) { int k=read(); if(k==1) { int x=read(),y=read(),z=read(); x^=opt*las_ans,y^=opt*las_ans; while (top[x]!=top[y]) { if(id[top[x]]<id[top[y]]) swap(x,y); T_add(id[top[x]],id[x],z); x=fa[top[x]]; } if(id[x]>id[y]) swap(x,y); T_add(id[x],id[y],z); } else if(k==2) { int x=read(),y=read(); x^=opt*las_ans,y^=opt*las_ans; las_ans=0; while (top[x]!=top[y]) { if(id[top[x]]<id[top[y]]) swap(x,y); las_ans+=query(id[top[x]],id[x]); x=fa[top[x]]; } if(id[x]>id[y]) swap(x,y); las_ans+=query(id[x],id[y]); printf("%d ",las_ans); } else { int x=read(); x^=opt*las_ans; las_ans=query(id[x],mx[x]); printf("%d ",las_ans); } } }
T2:
T3:
一开始想了个贪心,仔细想了想发现是错的。
首先对于环上的问题我们应该先思考链上怎么做然后想办法转换。
这个题如果是链上的话就直接$f_{i}$代表前$i$个线段覆盖的最右端点。
转移还是很好写的,不过记得$f_{i}=a_{i-1}+dis$的转移。
我们只考虑第一个点向右走,然后reverse再做一遍就好了。
细节见代码的分类讨论,原理就是这样$pos+1$之后的点向左走就不会对$index$更大的点影响了。
没开longlong拍了半天。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; typedef unsigned long long unll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=1e5+5; int m; ll n,a[M],f[M]; inline void dp(int x,ll len) { for1(x,m,i) { f[i]=f[i-1]; if(f[i]>=a[i]-1) f[i]=a[i]+len; else if(a[i]-len-1<=f[i]) { f[i]=a[i]; if(i-1>=x&&a[i]-len-1<=f[i-2]) f[i]=max(f[i],a[i-1]+len); } else { f[m]=-1e18; break; } } } inline bool check(ll len) { int pos=1; while (pos<m&&a[pos+1]<a[1]+len+1) ++pos; if(pos==1) { f[1]=a[1]+len; dp(2,len); return f[m]-n>=a[1]-1; } else if(pos==2) { if(m==2) { return a[2]+len-n>=a[1]-1||a[1]+len-n>=a[2]-len-1; } else { f[2]=max(a[2],a[1]+len); dp(3,len); if(f[m]-n>=min(a[1]-1,a[2]-len-1)) return 1; f[2]=a[2]+len; dp(3,len); return f[m]-n>=a[1]-1; } } else { f[pos]=a[pos]+len; dp(pos+1,len); return f[m]-n>=min(a[1]-1,a[2]-len-1); } } int main () { //freopen("a.in","r",stdin); //freopen("zj.out","w",stdout); cin>>n; m=read(); for1(1,m,i) scanf("%lld",a+i); sort(a+1,a+m+1); ll l=0,r=1e18,mid,ans; while (l<=r) { mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } for1(2,m,i) { if(a[i]==a[1]) a[i]=1; else a[i]=a[1]+n-a[i]+1; } a[1]=1; sort(a+1,a+m+1); l=0,r=ans-1; while (l<=r) { mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } cout<<ans<<endl; }
12.12:
T1:
也是暴搜$border$。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; typedef unsigned long long unll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=55; const int N=205; const int MAXN=1e5+5; const int mod=1e9+7; int n,m,A; int bin[N]; int size,shu[MAXN],dis[MAXN],nxt[MAXN][M],f[N][MAXN]; map <unll,int> vis; inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} struct node { int be[M]; inline void init() { for1(1,m,i) be[i]=i; } inline int find(int x) { if(x==be[x]) return x; return be[x]=find(be[x]); } inline void add_(int x) { for1(1,x,i) be[find(i)]=find(m-x+i); } inline int count() { int cnt=0; for1(1,m,i) cnt+=i==be[i]; return cnt; } inline unll hash_() { unll x=0; for1(1,m,i) x=x*mod+find(i); return x; } }; vector <node> sta[N]; signed main () { //freopen("a.in","r",stdin); //freopen("zj.out","w",stdout); n=read(),m=read(),A=read(); bin[0]=1; for1(1,n,i) bin[i]=1ll*A*bin[i-1]%mod; node t; t.init(); sta[1].push_back(t); size=dis[1]=vis[t.hash_()]=1; for1(1,n-m+1,i) { FOR2(sta[i].size(),1,j) { node x=sta[i][j-1]; int be=vis[x.hash_()]; if(dis[be]!=i) continue; shu[be]=x.count(); for1(1,m-1,k) { t=x; t.add_(k); unll ha=t.hash_(); if(vis.count(ha)) { if(i+m-k<dis[vis[ha]]) { dis[vis[ha]]=i+m-k; sta[i+m-k].push_back(t); } } else if(i+m-k<=n-m+1) { vis[ha]=++size; dis[size]=i+m-k; sta[dis[size]].push_back(t); } } } } for1(1,n-m+1,i) { FOR2(sta[i].size(),1,j) { node x=sta[i][j-1]; int be=vis[x.hash_()]; if(dis[be]!=i) continue; for1(1,m-1,k) { t=x; t.add_(k); unll ha=t.hash_(); nxt[be][k]=vis[ha]; } } } int ans=0; for1(1,n-m+1,i) f[i][1]=bin[i-1]; for1(1,n-m+1,i) { //cout<<i<<endl; for1(1,size,j) if(f[i][j]) { for1(i+1,n-m+1,k) { if(i+m-1>=k) { inc(f[k][nxt[j][i+m-k]],mod-f[i][j]); } else { inc(f[k][j],mod-1ll*f[i][j]*bin[k-i-m]%mod); } } // cout<<i<<" "<<j<<" "<<f[i][j]<<" "<<shu[j]<<endl; inc(ans,1ll*f[i][j]*bin[shu[j]+n-m+1-i]%mod); } } cout<<ans<<endl; }
T2:
非常好的题,为出题人点赞。
主要考察了对于容斥原理的使用。
首先重要的一点是$m=0$的部分。
题解非常牛逼直接搞到了$O(n^2*log(n))$。
我们考虑递归这$n$个数,并找到$max$。
1> $max=0$,直接返回$C$是否为0。
2> $left lfloor log_{2}x ight floor < left lfloor log_{2}C ight floor$,返回0。
3> $left lfloor log_{2}x ight floor geq left lfloor log_{2}C ight floor$,这样我们可以讨论$max$的最高位取啥。
(1)若最高位取1,我们直接递归下去,$max$^$=1<<i$,$C$^$=1<<i$
(2)若最高位取0,我们把这个数去掉,剩下的数只需要满足最高位和C的最高位一样就行了,剩下位我是随便选的。
所以这样就OK了。
一共可能递归$n*log(w)$次,每次有个线性dp。
然后我们考虑对于传统的枚举边状态的容斥。
我们会发现不同的边状态可能导致相同的点状态。
这就启发我们对于每一个相同的点集S,算出他的所有联通情况下的容斥系数。
这个可以通过枚举最小点所在集合大小减去不合法就行了。
然后就比较容易了,dp出$f[s][t]$代表点集为$s$,然后奇数集合的$val$最小的点的集合为$t$的方案数。
然后就可以直接搞了。
总复杂的$O(n^2*log(w)*2^n+4^n)$。
最后dp出f数组的复杂度是$O(4^n)$的。
#include <bits/stdc++.h> #include <unordered_map> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=20; const int N=(1<<16)+5; const int mod=998244353; int n,m; ll W,a[M]; bool vis[M]; int all,p[N],k[N],f[N],xi[N]; unordered_map <int,int> g[N]; struct node {int x,y;}e[M*M]; inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline int calc_(int len,vector <ll>&cc,ll to) { if(!len) return to==0; ll x=cc[0]; int t,pos=0,ans=0; for1(1,len-1,i) if(cc[i]>x) pos=i,x=cc[i]; if(x==0) return to==0; for1(0,60,i) if(x>>i&1) t=i; if(to>>t+1) return 0; vector <ll> sta; for1(0,len-1,i) sta.push_back(cc[i]); sta[pos]^=1ll<<t; ans=calc_(len,sta,to^(1ll<<t)); int f[2]={1,0}; for1(0,len-1,i) if(i!=pos) { ll z=cc[i]; if(!(z>>t)) { f[0]=(z+1)%mod*f[0]%mod; f[1]=(z+1)%mod*f[1]%mod; } else { int g[2]={0}; inc(g[0]=(1ll<<t)%mod*f[0]%mod,(z-(1ll<<t)+1)%mod*f[1]%mod); inc(g[1]=(1ll<<t)%mod*f[1]%mod,(z-(1ll<<t)+1)%mod*f[0]%mod); f[0]=g[0],f[1]=g[1]; } } inc(ans,f[to>>t&1]); return ans; } int main () { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); scanf("%d%d%lld",&n,&m,&W); for1(1,n,i) scanf("%lld",a+i); for1(1,m,i) scanf("%d%d",&e[i].x,&e[i].y); all=(1<<n)-1; for1(1,all,i) p[i]=p[i>>1]+(i&1); for1(0,all,i) { for1(1,n,j) vis[j]=i>>j-1&1; int cnt=0; for1(1,m,j) if(vis[e[j].x]&&vis[e[j].y]) ++cnt; k[i]=!cnt; } //for1(0,all,i) cout<<k[i]<<endl; //return 0; for1(0,all,i) { f[i]=k[i]; for(int j=i-1;j;j=(j-1)&i) if((j&-j)==(i&-i)&&k[i^j]) { inc(f[i],mod-f[j]); } } //for1(0,all,i) cout<<f[i]<<" "; cout<<endl; //return 0; for1(0,all,i) { ll Min=1e18; for1(1,n,j) if((i>>j-1&1)&&Min>a[j]) xi[i]=j,Min=a[j]; if(!(p[i]&1)) f[i]=(Min+1)%mod*f[i]%mod; } //for1(0,all,i) cout<<p[i]<<" "; cout<<endl; //for1(0,all,i) cout<<xi[i]<<" "; cout<<endl; //return 0; g[0][0]=1; for1(0,all-1,i) { int pos; FOR2(n,1,j) if(!(i>>j-1&1)) pos=j; int zt=i|(1<<pos-1); for(auto aa:g[i]) { int x=aa.first; int y=aa.second; for(int j=zt;j<=all;j=(j+1)|zt) { int t=j^i; if(p[t]&1) inc(g[j][x|(1<<xi[t]-1)],1ll*y*f[t]%mod); else inc(g[j][x],1ll*y*f[t]%mod); } } } int ans=0; for(auto x:g[all]) { int i=x.first; int j=x.second; vector <ll> cc; for1(1,n,u) if(i>>u-1&1) cc.push_back(a[u]); //cout<<i<<" "<<j<<endl; inc(ans,1ll*j*calc_(cc.size(),cc,W)%mod); } cout<<ans<<endl; }
T3:
抽象出一个二叉树的模型。
然后会发现其实深度和根颜色确定的二叉树每种颜色的个数是确定的。
然后直接dp一下就好了。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=3e5+5; const int mod=998244353; int n,opt,root,st[3]; int sa[M][3]; int bin[M],g[3][M],f[3][M][3]; struct bignum { int len; char a[M]; inline void in() { scanf("%s",a+1); len=strlen(a+1); for1(1,len,i) a[i]-='0'; reverse(a+1,a+len+1); } inline int calc() { int sum=0; FOR2(len,1,i) sum=(10ll*sum+a[i])%mod; return sum; } }a[3],l,r; inline int qpow(int x,int k,int mo) { int js=1; for(;k;k>>=1,x=1ll*x*x%mo) if(k&1) js=1ll*x*js%mo; return js; } inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline int check() { for1(0,2,i) f[i][0][i]=1; for1(1,n,i) for1(0,2,j) { int x=j,y=(j+1)%3; for1(0,2,k) f[j][i][k]=(f[x][i-1][k]+f[y][i-1][k])%mod; } int t[3]; for1(0,2,i) t[i]=a[i].calc(); for1(0,2,x) { bool not_=0; for1(0,2,i) if(t[i]!=f[x][n][i]) not_=1; if(!not_) return x; } return -1; } inline void dfs(int x,int dep) { if(!dep) { putchar(st[x]); return; } int ls=x,rs=(x+1)%3; if(sa[dep-1][ls]>sa[dep-1][rs]) swap(ls,rs); dfs(ls,dep-1),dfs(rs,dep-1); } inline void trans_(int x,int dep,bool L,bool R) { if(L&&R) return dfs(x,dep),void(); if(!dep) { putchar(st[x]); return; } int ls=x,rs=(x+1)%3; if(sa[dep-1][ls]>sa[dep-1][rs]) swap(ls,rs); if(L||(!L&&!l.a[dep])) trans_(ls,dep-1,L,R|(r.a[dep])); if(R||(!R&&r.a[dep])) trans_(rs,dep-1,L|(!l.a[dep]),R); } int main () { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); n=read(),opt=read(); for1(1,3,i) a[i%3].in(); st[0]='P',st[1]='R',st[2]='S'; root=check(); if(root==-1) return puts("-1"),0; for1(0,2,i) sa[0][i]=i; for1(1,n,i) { int t[3]={0}; for1(0,2,j) { int x=sa[i-1][j]; int y=sa[i-1][(j+1)%3]; if(x>y) swap(x,y); t[j]=x*3+y; } for1(0,2,j) { int cnt=0; for1(0,2,k) cnt+=t[k]<t[j]; sa[i][j]=cnt; } } if(opt!=2) { for1(0,n,i) bin[i]=qpow(233,qpow(2,i,mod-1),mod); for1(0,2,i) g[i][0]=st[i]; for1(1,n,i) for1(0,2,x) { int ls=x,rs=(x+1)%3; if(sa[i-1][ls]>sa[i-1][rs]) swap(ls,rs); g[x][i]=(g[ls][i-1]+1ll*bin[i-1]*g[rs][i-1])%mod; } printf("%d ",g[root][n]); } if(opt!=1) { l.in(),r.in(); trans_(root,n,0,0); } }
12.24:
T1:
一个裸的min-max容斥题啊。
学了就会了,还考了fft。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int N=55; const int M=2e5+5; const int max_len=1<<17; const int mod=998244353; int n,m; int fac[M],inv[M]; int f[N][M],A[M],B[M],G[2][M]; inline int qpow(int x,int k) { int t=1; for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) t=1ll*x*t%mod; return t; } inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;} inline int com(int x,int y) { if(x<y) return 0; return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod; } inline int get_len(int x) { int len=1; for(;len<2*x;len<<=1); return len; } inline void ntt(int *a,int len,int H) { for(int i=0,j=0;i<len;++i) { if(i>j) swap(a[i],a[j]); for(int t=len>>1;(j^=t)<t;t>>=1); } int u,t; for(int m=2;m<=len;m<<=1) { int p=m>>1; for(int i=0;i<p;++i) G[1][i]=G[0][H==1?max_len/m*i:max_len-max_len/m*i]; for(int i=0;i<len;i+=m) for(int j=0;j<p;++j) { u=a[i+j]; t=1ll*a[i+j+p]*G[1][j]%mod; a[i+j]=(u+t)%mod; a[i+j+p]=(u-t+mod)%mod; } } if(H==-1) { int inv_=qpow(len,mod-2); for1(0,len-1,i) a[i]=1ll*a[i]*inv_%mod; } } signed main () { //freopen("a.in","r",stdin); //freopen("b.out","w",stdout); cin>>n>>m; fac[0]=1; for1(1,M-5,i) fac[i]=1ll*i*fac[i-1]%mod; inv[M-5]=qpow(fac[M-5],mod-2); FOR2(M-5,1,i) inv[i-1]=1ll*i*inv[i]%mod; G[0][0]=1; for(int i=1,t=qpow(3,(mod-1)/max_len);i<=max_len;++i) G[0][i]=1ll*t*G[0][i-1]%mod; f[0][0]=1; for1(0,m-1,i) f[1][i]=inv[i]; for1(2,n-1,i) { int x=i>>1,y=i+1>>1; int len=get_len(max(x,y)*(m-1)+1); fill(A,A+len+1,0),fill(B,B+len+1,0); FOR2(max(x,y)*(m-1),0,j) A[j]=f[x][j],B[j]=f[y][j]; ntt(A,len,1),ntt(B,len,1); for1(0,len-1,j) A[j]=1ll*A[j]*B[j]%mod; ntt(A,len,-1); for1(0,len-1,j) f[i][j]=A[j]; } int ans=0; for1(1,n,i) { int sum=0; int inv_i=1ll*inv[i]*fac[i-1]%mod; int inv_fac=qpow(inv_i,m); for1(0,(i-1)*(m-1),j) { int step_=1ll*(j+m)*n%mod*inv_i%mod; int seq_cnt=1ll*f[i-1][j]*inv[m-1]%mod*fac[j+m-1]%mod; inc(sum,1ll*seq_cnt*step_%mod*inv_fac%mod); inv_fac=1ll*inv_i*inv_fac%mod; } sum=1ll*sum*com(n,i)%mod*i%mod; i&1?inc(ans,sum):inc(ans,mod-sum); } cout<<ans<<endl; }
12.25:
T1:
非常好的题,给出题人点赞。
首先竞赛图的性质非常显然。
难点在于怎么维护查询一个乱序序列的最大最小值。
我一直在想怎么维护区间的max,min。
但苦于插入一个点可能对后面所有的区间的x都加上1。
然后我就自闭了。
看了题解发现我们可以维护区间中maxid,minid。
这就比较妙了,因为这样插入一个点其实对其他点之间的大小关系是没有影响的。
然后就可以比较轻松的搞到$log^2$。
然后有一个黑科技就是维护double了,只能用在重量平衡树上,而且无旋treap不能。
比较好的一点是我们不用记录每个节点的[$l,r]$,其实只记录mid然后函数传参$[l,r]$就行了。
我写了替罪羊树,卡内存感觉很没意思。
#include <bits/stdc++.h> #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } const int M=2e5+5; const int N=6e5+5; const double alpa=0.6; double x_[2]; int n,xi,da,opt,len; int be[M],mx[N],mn[N],las[M],nxt[M]; inline int find(int x) { if(x==be[x]) return x; return be[x]=find(be[x]); } struct sp { sp *ch[2]; int size; double x; sp() {} }*null,*root,a[M],*pool[M]; inline sp **insert(sp *&x,double l,double r,int k,int id) { if(x==null) { x=&a[id]; a[id].size=1; a[id].x=(l+r)*0.5; a[id].ch[0]=a[id].ch[1]=null; return &null; } sp **ha=&null; int num=k-x->ch[0]->size-1; if(num>=0) ha=insert(x->ch[1],(l+r)*0.5,r,num,id); else ha=insert(x->ch[0],l,(l+r)*0.5,k,id); x->size=x->ch[0]->size+x->ch[1]->size+1; if(max(x->ch[0]->size,x->ch[1]->size)>x->size*alpa+5) { ha=&x; x_[0]=l,x_[1]=r; } return ha; } inline void trans_(sp *x) { if(x==null) return; trans_(x->ch[0]); pool[++len]=x; trans_(x->ch[1]); } inline sp *rebuild(double lx,double rx,int l,int r) { if(l>r) return null; int mid=l+r>>1; double midx=(lx+rx)*0.5; pool[mid]->x=midx; pool[mid]->ch[0]=rebuild(lx,midx,l,mid-1); pool[mid]->ch[1]=rebuild(midx,rx,mid+1,r); pool[mid]->size=pool[mid]->ch[0]->size+pool[mid]->ch[1]->size+1; return pool[mid]; } inline void insert(int id,int k) { sp **pos=insert(root,0,1,k,id); if((*pos)!=null) { len=0; trans_(*pos); *pos=rebuild(x_[0],x_[1],1,len); } } inline int get_rank(double x) { x-=1e-10; int sum=0; sp *t=root; while (t!=null) { if(x<t->x) t=t->ch[0]; else sum+=t->ch[0]->size+1,t=t->ch[1]; } return sum+1; } inline void query(int g,int l,int r,int lx,int rx,bool t) { if(lx<=l&&rx>=r) { if(!t&&mn[g]&&(!xi||a[xi].x>a[mn[g]].x)) xi=mn[g]; if(t==1&&mx[g]&&(!da||a[da].x<a[mx[g]].x)) da=mx[g]; return; } int mid=l+r>>1; if(lx<=mid) query(g<<1,l,mid,lx,rx,t); if(rx>mid) query(g<<1|1,mid+1,r,lx,rx,t); } inline void T_add(int g,int l,int r,int x) { if(l==r) return mn[g]=mx[g]=x,void(); if(!mn[g]||a[x].x<a[mn[g]].x) mn[g]=x; if(!mx[g]||a[mx[g]].x<a[x].x) mx[g]=x; int mid=l+r>>1; if(x<=mid) T_add(g<<1,l,mid,x); else T_add(g<<1|1,mid+1,r,x); } /* inline void out(int x) { int root=0; for1(1,x,i) if(find(i)==i&&!las[i]) root=i; while (root) cout<<root<<" ",root=nxt[root]; puts(""); } inline int travel_(sp *x,int dep) { if(x==null) return 0; int ans=dep; ans=max(ans,travel_(x->ch[0],dep+1)); printf("%d ",x->x); ans=max(ans,travel_(x->ch[1],dep+1)); return ans; } */ signed main () { //freopen("a.in","r",stdin); //freopen("b.out","w",stdout); null=new sp(); null->size=0; root=null; /* for1(1,200000,i) insert(i,0); for1(1,200000,i) T_add(1,1,200000,i); for1(1,300000,i) { int l=rand()%200000+1; int r=rand()%200000+1; if(l>r) swap(l,r); query(1,1,200000,l,r,0); } for1(1,200000,i) get_rank(1.0*rand()/RAND_MAX); cout<<(double)clock()/CLOCKS_PER_SEC<<endl; return 0; */ int ans=0; n=read(),opt=read(); for1(1,n,i) { da=xi=0; int pre=0,t=read(); vector <pair<int,int> > cc; for1(1,t,j) { int l=read(),r=read(); if(opt) l=(l+ans-1)%i+1,r=(r+ans-1)%i+1; cc.push_back(make_pair(l,r)); } sort(cc.begin(),cc.end()); for1(1,t,j) { int l=cc[j-1].first,r=cc[j-1].second; query(1,1,n,l,r,0); if(pre+1<l) query(1,1,n,pre+1,l-1,1); pre=r; } if(pre+1<i) query(1,1,n,pre+1,i-1,1); be[i]=i; if(da) da=find(da); if(xi) xi=find(xi); if(da&&xi&&(da==xi||a[xi].x<a[da].x)) { xi=xi,da=da; insert(i,get_rank(a[xi].x)-1); int pos=xi; while (pos!=da) --ans,be[pos]=i,pos=nxt[pos]; be[da]=i; las[i]=las[xi]; nxt[i]=nxt[da]; if(las[i]) nxt[las[i]]=i; if(nxt[i]) las[nxt[i]]=i; } else { ++ans; if(!xi) { las[i]=da; if(las[i]) nxt[las[i]]=i; insert(i,i-1); } else { las[i]=da,nxt[i]=xi; if(las[i]) nxt[las[i]]=i; if(nxt[i]) las[nxt[i]]=i; insert(i,get_rank(a[xi].x)-1); } } T_add(1,1,n,i); if(i!=n) printf("%d ",ans); else printf("%d ",ans); /* cout<<"hahaha:"<<i<<endl; out(i); travel_(root,0); cout<<endl; cout<<endl; if(i%10000==0) cout<<(double)clock()/CLOCKS_PER_SEC<<endl; */ } }