【考試總結】2022-05-04
阿新 • • 發佈:2022-05-06
路人女主
勢不兩立的串對與以下兩種形成雙射(下述 \(ABC\) 是其六種輪換之一):
-
S=ABABABA
,\(T\) 的奇數位置是 \(C\),偶數位置是 \(A/B\) -
\(S,T\) 的偶數位置是 \(C\),奇數位置滿足存在一個 \(k\) 使得 \(s\) 的前 \(k\) 個奇數位置為 \(A\),後面的奇數位置為 \(B\),而 \(t\) 恰好相反:前 \(k\) 個奇數位置為 \(B\),後面的奇數位置為 \(A\)
Code Display
char s[N<<1],t[N<<1]; int ans,n; bool sufs[N<<1],suft[N<<1]; inline bool check(char A,char B){return A==B||A=='?';} inline void solve(char A,char B,char C){ bool Fail=0; int m=n<<1|1; for(int i=1;i<=m&&!Fail;++i){ if((i&1)&&!check(s[i],A)) Fail=1; if(!(i&1)&&!check(s[i],B)) Fail=1; } for(int i=1;i<=m&&!Fail;i+=2) if(!check(t[i],C)) Fail=1; for(int i=2;i<=m&&!Fail;i+=2) if(!check(t[i],A)&&!check(t[i],B)) Fail=1; if(!Fail){ int cnt=0; for(int i=2;i<=m;i+=2) cnt+=t[i]=='?'; ckadd(ans,ksm(2,cnt)); }else Fail=0; for(int i=1;i<=m&&!Fail;++i){ if((i&1)&&!check(t[i],A)) Fail=1; if(!(i&1)&&!check(t[i],B)) Fail=1; } for(int i=1;i<=m&&!Fail;i+=2) if(!check(s[i],C)) Fail=1; for(int i=2;i<=m&&!Fail;i+=2) if(!check(s[i],A)&&!check(s[i],B)) Fail=1; if(!Fail){ int cnt=0; for(int i=2;i<=m;i+=2) cnt+=s[i]=='?'; ckadd(ans,ksm(2,cnt)); }else Fail=0; for(int i=2;i<=m&&!Fail;i+=2) if(!check(s[i],C)||!check(t[i],C)) Fail=1; sufs[m+2]=suft[m+2]=1; for(int i=m;i>=1;i-=2){ sufs[i]=sufs[i+2]&check(s[i],B); suft[i]=suft[i+2]&check(t[i],A); } for(int i=1;i<m;i+=2){ Fail|=!check(s[i],A); Fail|=!check(t[i],B); if(Fail) break; if(sufs[i+2]&&suft[i+2]) ckadd(ans,1); } Fail|=!check(s[m],A); Fail|=!check(t[m],B); ckdel(ans,!Fail); return ; } signed main(){ freopen("a.in","r",stdin); freopen("a.out","w",stdout); int T=read(); while(T--){ n=read(); ans=0; scanf("%s%s",s+1,t+1); solve('C','B','A'); solve('C','A','B'); solve('B','C','A'); solve('B','A','C'); solve('A','B','C'); solve('A','C','B'); print(ans); } return 0; }
鈴原露露
對於樹上三個點 \(\rm z=lca(x,y)\) 那麼如果 \(a_z<a_x<a_y\) 那麼對於 \(l\in(a_z,a_x],r\in[a_y,n]\) 這樣子的區間 \([l,r]\) 都是非法的,類似的 \(a_x<a_y<a_z\) 的結構會導致 \(l\in[1,a_x],r\in[a_y,a_z)\) 不合法
那麼可以使用啟發式合併維護所有的 \(a_x\) 想要找到上面描述的矩形利用覆蓋關係所需要的就是每個元素在其它子樹裡面的前驅後繼
剩下的工作是一個線段樹維護歷史為 \(0\) 的次數的和,而這是平凡的
Code Display
const int N=2e5+10; set<int> sub[N]; int n,Q; struct Segment_Tree{ #define ls p<<1 #define rs p<<1|1 #define lson p<<1,l,mid #define rson p<<1|1,mid+1,r int num[N<<2],sum[N<<2],Mn[N<<2]; int plu[N<<2],tim[N<<2]; inline void push_up(int p){ sum[p]=sum[ls]+sum[rs]; Mn[p]=min(Mn[ls],Mn[rs]); num[p]=0; if(Mn[p]==Mn[ls]) num[p]+=num[ls]; if(Mn[p]==Mn[rs]) num[p]+=num[rs]; return ; } inline void push_add(int p,int v){plu[p]+=v,Mn[p]+=v;} inline void push_tim(int p,int v){sum[p]+=num[p]*v,tim[p]+=v;} inline void push_down(int p){ if(tim[p]){ int Min=min(Mn[ls],Mn[rs]); if(Min==Mn[ls]) push_tim(ls,tim[p]); if(Min==Mn[rs]) push_tim(rs,tim[p]); tim[p]=0; } if(plu[p]){ push_add(ls,plu[p]); push_add(rs,plu[p]); plu[p]=0; } return ; } inline void upd(int st,int ed,int v,int p=1,int l=1,int r=n){ if(st<=l&&r<=ed) return push_add(p,v); int mid=(l+r)>>1; push_down(p); if(st<=mid) upd(st,ed,v,lson); if(ed>mid) upd(st,ed,v,rson); return push_up(p); } inline int query(int st,int ed,int p=1,int l=1,int r=n){ if(st<=l&&r<=ed) return sum[p]; int mid=(l+r)>>1,res=0; push_down(p); if(st<=mid) res+=query(st,ed,lson); if(ed>mid) res+=query(st,ed,rson); return res; } inline void incr(int st,int ed,int p=1,int l=1,int r=n){ if(st<=l&&r<=ed) return push_tim(p,Mn[p]==0); int mid=(l+r)>>1; push_down(p); if(st<=mid) incr(st,ed,lson); if(ed>mid) incr(st,ed,rson); return push_up(p); } inline void build(int p,int l,int r){ num[p]=r-l+1; if(l==r) return ; int mid=(l+r)>>1; build(lson); build(rson); } #undef ls #undef rs #undef lson #undef rson }T; int a[N],ans[N]; vector<pair<int,int> >qu[N]; vector<int> G[N]; vector<tuple<int,int,int> >squ[N]; inline void ins(int sx,int ex,int sy,int ey){ squ[sy].emplace_back(sx,ex,1); squ[ey+1].emplace_back(sx,ex,-1); } inline void dfs(int x){ int son=0; for(auto t:G[x]){ dfs(t); if(sub[t].size()>sub[son].size()) son=t; } swap(sub[x],sub[son]); for(auto t:G[x]) if(t!=son){ for(auto v:sub[t]){ auto iter=sub[x].upper_bound(v); if(v>a[x]){ if(iter!=sub[x].end()){ ins(a[x]+1,v,*iter,n); } if(iter!=sub[x].begin()){ --iter; if(*iter>a[x]){ ins(a[x]+1,*iter,v,n); } } }else{ if(iter!=sub[x].end()){ if(*iter<a[x]){ ins(1,v,*iter,a[x]-1); } } if(iter!=sub[x].begin()){ iter--; ins(1,*iter,v,a[x]-1); } } } for(auto v:sub[t]) sub[x].insert(v); } sub[x].insert(a[x]); return ; } int his[N],val[N]; signed main(){ freopen("b.in","r",stdin); freopen("b.out","w",stdout); n=read(); Q=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=2;i<=n;++i) G[read()].emplace_back(i); for(int i=1;i<=Q;++i){ int l=read(),r=read(); qu[r].emplace_back(l,i); } dfs(1); T.build(1,1,n); for(int i=1;i<=n;++i){ for(auto opt:squ[i]){ int l,r,v; tie(l,r,v)=opt; T.upd(l,r,v); } T.incr(1,i); for(auto q:qu[i]) ans[q.sec]=T.query(q.fir,i); } for(int i=1;i<=Q;++i) print(ans[i]); return 0; }
赫露艾斯塔
這個題本質上是在求一個半平面上偏序對數量
注意到直線斜率不超過 \(0\) 時可以預處理出來每個點被偏序以及偏序其它元素的次數然後做半平面上的元素求和
而對於直線斜率為正的情況,此時不構成偏序的元素是具有傳遞性的,所以可以進行容斥計算
對於半平面上權值和這一問題可以將每個維度分成 \(\sqrt {\rm n}\) 個塊,並對每行的塊做字首和
對於一條直線,它橫穿的塊的個數不超過 \(\sqrt n\),而又在資料隨機的情況下每個塊裡面期望 \(\Theta(1)\) 個點,所以可以暴力掃
Code Display
const int D=1e4+1,N=2e5+10; int bel[N],lef[N],rig[N]; struct Fenwick_Tree{ int c[N]; inline void insert(int x,int v){ x+=D; for(;x<=(D<<1);x+=x&(-x)) c[x]+=v; return ; } inline int query(int x){ x+=D; int res=0; for(;x;x-=x&(-x)) res+=c[x]; return res; } inline int query(int l,int r){return query(r)-query(l-1);} inline void clear(){memset(c,0,sizeof(c));} }T; const int BL=150,Bcnt=200,Cmax=1e4; ll A,B,C; int n,Q; pair<int,int> cor[N]; double k,b; namespace spj{ int pre[N],suf[N]; inline void init(){ for(int i=1;i<=n;++i){ int j=i; while(j<=n&&cor[i].fir==cor[j].fir) ++j; --j; for(int k=i;k<=j;++k) pre[k]=pre[k-1]+T.query(-1e4,cor[k].sec-1); for(int k=i;k<=j;++k) T.insert(cor[k].sec,1); i=j; } T.clear(); for(int i=n;i>=1;--i){ int j=i; while(j&&cor[j].fir==cor[i].fir) --j; ++j; for(int k=i;k>=j;--k) suf[k]=suf[k+1]+T.query(cor[k].fir+1,1e4); for(int k=i;k>=j;--k) T.insert(cor[k].sec,1); i=j; } T.clear(); } inline void solve(){ if(A<0){ int l=1,r=n,ans=0; while(l<=r){ int mid=(l+r)>>1; if(cor[mid].fir*A+C>0) ans=mid,l=mid+1; else r=mid-1; } print(pre[ans]); }else{ int l=1,r=n,ans=n+1; while(l<=r){ int mid=(l+r)>>1; if(cor[mid].fir*A+C>0) ans=mid,r=mid-1; else l=mid+1; } print(suf[ans]); } } } inline int get_block(ll x){ ll y=k*x+b; if(y<-Cmax) return 0; if(y>Cmax) return bel[Cmax+D]+1; return bel[y+D]; } vector<int> node[Bcnt][Bcnt]; struct Block{ ll sum[Bcnt][Bcnt]; int w[N]; inline void init(){ for(int i=1;i<=n;++i) sum[bel[cor[i].fir+D]][bel[cor[i].sec+D]]+=w[i]; for(int i=1;i<=bel[Cmax+D];++i){ for(int j=bel[Cmax+D];j>=1;--j) sum[i][j]+=sum[i][j+1]; } return ; } }cnt; struct Block1:Block{ inline void prework(){ for(int i=n;i>=1;--i){ int j=i; while(cor[i].fir==cor[j].fir&&j) --j; ++j; for(int k=i;k>=j;--k) w[k]=T.query(cor[k].sec+1,Cmax); for(int k=i;k>=j;--k) T.insert(cor[k].sec,1); i=j; } T.clear(); init(); } inline void solve(){ ll ans=0; for(int i=1;i<=bel[Cmax+D];++i){ int l=get_block(lef[i]),r=get_block(rig[i]); if(r<l) swap(l,r); ans+=sum[i][r+1]; for(int j=l;j<=r;++j) for(auto cur:node[i][j]){ if(cor[cur].fir*A+cor[cur].sec*B+C>0) ans+=w[cur]; } } print(ans); } }plain1; struct Block2:Block{ ll all=0; inline void prework(){ for(int i=1;i<=n;++i){ int j=i; while(cor[i].fir==cor[j].fir&&j<=n) ++j; --j; for(int k=i;k<=j;++k) w[k]=T.query(-Cmax,cor[k].sec-1); for(int k=i;k<=j;++k) T.insert(cor[k].sec,1); i=j; } rep(i,1,n) all+=w[i]; T.clear(); init(); } inline void solve(){ ll ans=all; for(int i=1;i<=bel[Cmax+D];++i){ int l=get_block(lef[i]),r=get_block(rig[i]); if(r<l) swap(l,r); ans-=sum[i][r+1]; for(int j=l;j<=r;++j) for(auto cur:node[i][j]){ if(cor[cur].fir*A+cor[cur].sec*B+C<=0) ans-=w[cur]; } } print(ans); } }plain2; struct Block3:Block{ inline void prework(){ for(int i=1;i<=n;++i){ int j=i; while(cor[i].fir==cor[j].fir&&j<=n) ++j; --j; Down(k,j,i) w[k]=T.query(cor[k].sec,Cmax),T.insert(cor[k].sec,1); i=j; } T.clear(); init(); } inline void solve(){ ll num=0,ans=0; for(int i=1;i<=bel[Cmax+D];++i){ int l=get_block(lef[i]),r=get_block(rig[i]); if(r<l) swap(l,r); ans+=sum[i][r+1]; num+=cnt.sum[i][r+1]; for(int j=l;j<=r;++j) for(auto cur:node[i][j]){ if(cor[cur].fir*A+cor[cur].sec*B+C>0) ans+=w[cur],num++; } } print(num*(num-1)/2-ans); } }plain3; struct Block4:Block{ ll all=0; inline void prework(){ for(int i=n;i>=1;--i){ int j=i; while(cor[i].fir==cor[j].fir&&j) --j; ++j; for(int k=j;k<=i;++k) w[k]=T.query(-Cmax,cor[k].sec),T.insert(cor[k].sec,1); i=j; } rep(i,1,n) all+=w[i]; T.clear(); init(); } inline void solve(){ ll ans=all,num=n; for(int i=1;i<=bel[Cmax+D];++i){ int l=get_block(lef[i]),r=get_block(rig[i]); if(r<l) swap(l,r); ans-=sum[i][r+1]; num-=cnt.sum[i][r+1]; for(int j=l;j<=r;++j) for(auto cur:node[i][j]){ if(cor[cur].fir*A+cor[cur].sec*B+C<=0) num--,ans-=w[cur]; } } print(num*(num-1)/2-ans); } }plain4; signed main(){ freopen("c.in","r",stdin); freopen("c.out","w",stdout); for(int i=-Cmax;i<=Cmax;++i) bel[i+D]=(i+D-1)/BL+1; lef[1]=-Cmax; for(int i=-Cmax+1;i<=Cmax;++i) if(bel[i+D]!=bel[i-1+D]) lef[bel[i+D]]=i,rig[bel[i+D-1]]=i-1; rig[bel[Cmax+D]]=Cmax; n=read(); Q=read(); for(int i=1;i<=n;++i) cor[i].fir=read(),cor[i].sec=read(); sort(cor+1,cor+n+1); spj::init(); for(int i=1;i<=n;++i){ node[bel[cor[i].fir+D]][bel[cor[i].sec+D]].emplace_back(i); cnt.w[i]=1; } cnt.init(); plain1.prework(); plain2.prework(); plain3.prework(); plain4.prework(); while(Q--){ A=read(),B=read(),C=read(); if(!B) spj::solve(); else{ k=-1.0*A/B,b=-1.0*C/B; if(k<0){ if(B>0) plain1.solve(); else plain2.solve(); }else{ if(B>0) plain3.solve(); else plain4.solve(); } } } return 0; }