CF题目泛做 2
代码已更。
[] CF1463E
缩点拓扑
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; vector<int> vec[MAXN],vec1[MAXN],C[MAXN],Ans; int N,K,d[MAXN],col[MAXN],X[MAXN],Y[MAXN],fa[MAXN],vis[MAXN]; queue<int> que; void print(){printf("0\n");exit(0);} void ins(int x){for(auto v:C[x]) Ans.pb(v);return;} void dfs(int u){col[u]=col[0];C[col[0]].pb(u); for(auto v:vec1[u]){if(col[v]) print();dfs(v);}return;} int main(){ //freopen("1.in","r",stdin); N=read(),K=read(); for(int i=1;i<=N;i++) X[i]=read(),Y[i]=i,fa[i]=X[i]; for(int i=1;i<=K;i++) {int u=read(),v=read();d[v]++;vec1[u].pb(v);} for(int i=1;i<=K;i++) if(vec1[i].size()>1) print(); for(int i=1;i<=N;i++){ if(!d[i]){col[0]++; dfs(i);} } memset(d,0,sizeof(d)); for(int i=1;i<=N;i++) if(col[X[i]]!=col[Y[i]]&&X[i]) vec[col[X[i]]].pb(col[Y[i]]),d[col[Y[i]]]++; for(int i=1;i<=col[0];i++) if(!d[i]) que.push(i),ins(i); while(!que.empty()){ int xx=que.front(); que.pop(); for(auto v:vec[xx]){ d[v]--; if(!d[v]){que.push(v),ins(v);} } } if(Ans.size()!=N) print(); vis[0]=1; for(auto v:Ans){ if(!vis[fa[v]]) print(); vis[v]=1; } for(auto v:Ans) printf("%d ",v);printf("\n"); return 0; }
[] CF1455E
发现边长只能是 $4$ 个点坐标差的其中一个,暴力求解即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<climits> #include<set> #define int long long #define pii pair<int,int> #define fi first #define se second #define pb push_back #define mp make_pair using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int T,A[5],B[5],tmp[5]; int Q1(int x0,int x1,int x2,int x3){ tmp[0]=0;tmp[++tmp[0]]=x0,tmp[++tmp[0]]=x1,tmp[++tmp[0]]=x2,tmp[++tmp[0]]=x3; sort(tmp+1,tmp+tmp[0]+1);return tmp[4]-tmp[1]+tmp[3]-tmp[2]; } int p[5],F[5],b[5],R,ss,inf=LLONG_MAX; void dfs(int u){ if(u==5){ for(int i=1;i<=4;i++) F[i]=A[p[i]]; F[2]-=R,F[4]-=R;int sum=Q1(F[1],F[2],F[3],F[4]); for(int i=1;i<=4;i++) F[i]=B[p[i]]; F[3]-=R,F[4]-=R; sum+=Q1(F[1],F[2],F[3],F[4]); ss=min(ss,sum); return; } for(int i=1;i<=4;i++){ if(!b[i]){ p[u]=i;b[i]=1;dfs(u+1);b[i]=0; } }return; } int Q(int r){R=r;ss=inf;dfs(1);return ss;} signed main(){ //freopen("2.in","r",stdin); T=read(); while(T--){ for(int i=1;i<=4;i++) A[i]=read(),B[i]=read(); int Minn=inf; for(int i=1;i<=4;i++){ for(int j=i+1;j<=4;j++) Minn=min(Minn,min(Q(B[j]-B[i]),Q(A[j]-A[i]))); } printf("%lld\n",Minn+1-1); }return 0; }/* 1 6 2 2 2 5 4 1 */
[] CF1452E
若对线段按中点排序可以发先前缀是 $i$ ,后缀是 $j$ ,$i,j$ 为老师在的地方,证明考虑最优性。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; int S[MAXN][MAXN],N,M,K,Maxn; pii f[MAXN]; bool cmp(pii x1,pii x2){return x1.fi+x1.se<x2.fi+x2.se;} int main(){ //freopen("3.in","r",stdin); N=read(),M=read(),K=read(); for(int i=1;i<=M;i++) f[i].fi=read(),f[i].se=read(); sort(f+1,f+M+1,cmp); for(int i=1;i<=M+1;i++){ for(int j=1;j+K-1<=N;j++){ int l=j,r=j+K-1,L=max(f[i].fi,l),R=min(f[i].se,r); int w=0; if(L<=R) w=R-L+1; S[i][j]=S[i-1][j]+w; } } for(int i=1;i<=M;i++){ int Maxn1=0,Maxn2=0; for(int j=1;j<=N;j++) Maxn1=max(Maxn1,S[i][j]),Maxn2=max(Maxn2,S[M+1][j]-S[i][j]); Maxn=max(Maxn,Maxn1+Maxn2); }printf("%d\n",Maxn); return 0; }
[x] CF1446F1
若 $1-n$ 中存在超过两个众数肯定答案是 $n$ ,否则设众数为 $x$ 。
最后答案序列肯定有一个众数是 $x$ ,证明考虑反证法,设当前最优区间为 $[l,r]$ ,且 $x$ 不是其中的众数。
那么我们在 $[l-1,r],[l-2,r],...,[1,r],[1,r+1],[1,r+2],...,[1,n]$ 中肯定会出现 $x$ 与其中一个数共为众数,且当前长度大于 $r-l+1$ 。
则枚举 $x$ 暴力计算。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #include<map> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; int N,A[MAXN],S[MAXN],Num[MAXN],Maxn,Ans,pre[MAXN<<1]; int main(){ N=read(); for(int i=1;i<=N;i++) A[i]=read(),Num[A[i]]++; int Maxn=0; for(int i=1;i<=N;i++) Maxn=max(Maxn,Num[i]); int cnt=0; for(int i=1;i<=100;i++) cnt+=(Maxn==Num[i]); if(cnt>1){printf("%d\n",N);return 0;} for(int i=1;i<=N;i++) if(Num[i]==Maxn){Maxn=i;break;} for(int i=1;i<=100;i++){ if(i!=Maxn){ int ss=0;memset(pre,-1,sizeof(pre));pre[0+N]=0; for(int j=1;j<=N;j++){ int w; if(A[j]==Maxn) w=1; else if(A[j]==i) w=-1; else w=0; ss+=w; if(pre[ss+N]!=-1) Ans=max(Ans,j-pre[ss+N]); else pre[ss+N]=j; } } }printf("%d\n",Ans); return 0; }
[x] CF1444C
不会并查集了。二分图的充要条件是存在不存在奇环,而奇环的判定可以那扩域并查集维护。
为了支持还原用可撤销并查集维护即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<climits> #include<stack> #include<vector> #define LL long long #define pii pair<int,int> #define fi first #define se second #define pb push_back #define mp make_pair using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; int N,M,K,A[MAXN]; vector<pair<int,pii> > vec[MAXN]; vector<pii> E[MAXN]; bool F[MAXN]; struct Union{ stack<pii> sta;int siz[MAXN],f[MAXN]; int Get(){return sta.size();} void init(){for(int i=1;i<=2*N;i++) f[i]=i,siz[i]=1;while(!sta.empty()) sta.pop();return;} int find(int x){return f[x]==x?x:find(f[x]);} void merge(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return;if(siz[t1]<siz[t2]) swap(t1,t2),swap(x,y); f[t2]=t1,siz[t1]+=siz[t2]; sta.push(mp(t2,t1));return; } void Back(){ pii s=sta.top();sta.pop(); f[s.fi]=s.fi,siz[s.se]-=siz[s.fi]; return; } void print(){ for(int i=1;i<=N*2;i++) printf("%d ",find(i));printf("\n"); for(int i=1;i<=N*2;i++) printf("%d ",siz[i]);printf("\n"); return; } }S; int Sta[MAXN];bool GG[MAXN]; int main(){ //freopen("maker.in","r",stdin); N=read(),M=read(),K=read();S.init(); for(int i=1;i<=N;i++) A[i]=read(); for(int i=1;i<=M;i++){ int u=read(),v=read(); if(A[u]==A[v]){ S.merge(u,v+N);S.merge(v,u+N); if(S.find(u)==S.find(v)) F[A[u]]=1; E[A[u]].pb(mp(u,v)); }else vec[A[u]].pb(mp(A[v],mp(v,u))),vec[A[v]].pb(mp(A[u],mp(u,v))); //cerr<<"->"<<u<<" v:"<<v<<endl; }int cnt=0;for(int i=1;i<=K;i++) cnt+=(!F[i]); for(int i=1;i<=K;i++) sort(vec[i].begin(),vec[i].end()); if(cnt<=1){printf("0\n");return 0;}LL Ans=(LL)(cnt*(LL)(cnt-1))/2ll; S.init();int cc=0; for(int i=1;i<=K;i++){ for(auto pp:E[i]) S.merge(pp.fi,pp.se+N),S.merge(pp.se,pp.fi+N); } //S.print(); for(int Col=1;Col<=K;Col++){ if(F[Col]) continue; int las=-1,siz=S.Get(); //S.print(); //return 0; for(auto pp:vec[Col]){ int col=pp.fi,v=pp.se.fi,u=pp.se.se; if(F[col]) continue; if(las!=col) while(S.Get()!=siz) S.Back(); S.merge(u,v+N),S.merge(v,u+N); if(S.find(u)==S.find(v)&&!GG[col]){ GG[col]=1;Sta[++Sta[0]]=col; cc++; } las=col; } while(S.Get()!=siz) S.Back(); while(Sta[0]) GG[Sta[Sta[0]]]=0,Sta[0]--; //return 0; } printf("%lld\n",Ans-cc/2);return 0; }/* 6 6 3 2 1 1 3 3 3 4 3 6 5 4 2 1 6 5 1 2 3 */
[] CF1443E
由于最后排名不会超过 $2\times 10^{10}$ ,则我们只需要暴力康拓展开后 $15$ 位即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<vector> #include<queue> #include<algorithm> #include<climits> #include<set> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second #define Vec vector<int> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; int N,fac[16],Q,sum; Vec calc(int res){ Vec Ans;Ans.clear(); set<int> s; set<int>::iterator it; for(int i=1;i<=min(N,15ll);i++) s.insert(i); for(int i=1;i<=min(N,15ll);i++){ it=s.begin(); int x=*it; while(res>=fac[min(N,15ll)-i]){it++;res-=fac[min(N,15ll)-i];} Ans.pb(*it);s.erase(*it); }return Ans; } signed main(){ //freopen("4.in","r",stdin); N=read(),Q=read(); fac[0]=1;for(int i=1;i<=15;i++) fac[i]=fac[i-1]*i; while(Q--){ int opt=read(); if(opt==1){ int l=read(),r=read(); if(N<=15){ Vec Ans=calc(sum); int ss=0,res=0; //for(auto v:Ans) printf("%lld ",v); printf("\n"); for(auto v:Ans){++ss;if(ss>=l&&ss<=r) res+=v;} printf("%lld\n",res); }else{ Vec Ans=calc(sum); int ps=N-15+1,res=0; for(auto v:Ans){if(l<=ps&&ps<=r) res+=v+(N-15);ps++;} r=min(r,N-15); if(l<=r) res+=((l+r)*(r-l+1))/2; printf("%lld\n",res); } }else sum+=read(); } }
[] CF1442C
可以发现当 $k$ 过大时若存在更小 $k'$ 可以从 $1$ 走到 $n$ 则我们肯定选择 $k'$ 。
所以 $k$ 的阀值为 $18(2^{18}\geq 2\times 10^5)$ 。若在 $18$ 次以内无解则对找到最短的 $k$ 使得可以从 $1$ 走到 $n$ ,做一个类似洪水填充的东西。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define mod 998244353 #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; int N,M,dis[19][MAXN],vis[19][MAXN],inf; vector<int> vec[2][MAXN]; priority_queue<pair<int,pii> > que; queue<int> Q1,Q2; void dijkstra(){ memset(dis,127/3,sizeof(dis)); inf=dis[0][0]; dis[0][1]=0;que.push(mp(0,mp(0,1))); while(!que.empty()){ int t=que.top().se.fi,u=que.top().se.se; que.pop(); if(vis[t][u]) continue; vis[t][u]=1; if(t<18){ if(dis[t+1][u]>dis[t][u]+(1<<t)) dis[t+1][u]=dis[t][u]+(1<<t),que.push(mp(-dis[t+1][u],mp(t+1,u))); } for(auto v:vec[t&1][u]){ if(dis[t][v]>dis[t][u]+1){dis[t][v]=dis[t][u]+1;que.push(mp(-dis[t][v],mp(t,v)));} } }return; } int diss[MAXN],sta[MAXN]; int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} signed main(){ N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[0][u].pb(v),vec[1][v].pb(u);} dijkstra(); int Minn=inf; for(int i=0;i<=18;i++) Minn=min(Minn,dis[i][N]); if(Minn!=inf){printf("%lld\n",Minn);return 0;} memset(diss,127/3,sizeof(diss)); diss[1]=0,Q1.push(1),Q2.push(1); for(int t=0;;t++){ while(!Q1.empty()){ int xx=Q1.front(); Q1.pop(); if(xx==N){printf("%lld\n",(ksm(2,t)-1+diss[xx]+mod)%mod);return 0;} for(auto v:vec[t&1][xx]){ if(diss[v]>diss[xx]+1) Q1.push(v),Q2.push(v),diss[v]=diss[xx]+1; } }while(!Q2.empty()) Q1.push(Q2.front()),Q2.pop(); }return 0; }
[] CF1439C
现将询问的位置从 $x$ 拉到 $1$,代价加上 $\sum_{i=1}^{x-1} A_i$ 即可。
我们发现连续段个数肯定不超过 $\log n$ 个,因为每次减少量至少为原来的一半。
线段树二分即可,时间复杂度 $O(n\log^2 n)$ 。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<climits> #include<set> #define int long long #define pii pair<int,int> #define fi first #define se second #define pb push_back #define mp make_pair using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; int N,Q,A[MAXN]; struct Segment{ int Minn[MAXN<<2],Sum[MAXN<<2],tag[MAXN<<2]; void pushdown(int k,int l,int r){ if(tag[k]==-1) return;int mid=(l+r)>>1; Minn[k<<1]=Minn[k<<1|1]=tag[k]; Sum[k<<1]=(mid-l+1)*tag[k],Sum[k<<1|1]=(r-mid)*tag[k]; tag[k<<1]=tag[k],tag[k<<1|1]=tag[k]; tag[k]=-1;return; } void pushup(int k){Minn[k]=min(Minn[k<<1],Minn[k<<1|1]);Sum[k]=Sum[k<<1]+Sum[k<<1|1];return;} void build(int k,int l,int r){ tag[k]=-1;if(l==r){Minn[k]=Sum[k]=A[l];return;} int mid=(l+r)>>1;build(k<<1,l,mid),build(k<<1|1,mid+1,r); pushup(k);return; } void Modify(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){tag[k]=Minn[k]=w;Sum[k]=(r-l+1)*w;return;} pushdown(k,l,r); int mid=(l+r)>>1; if(x<=mid) Modify(k<<1,l,mid,x,y,w);if(mid<y) Modify(k<<1|1,mid+1,r,x,y,w); pushup(k); return; } int Query(int k,int l,int r,int ps){ if(l==r) return Minn[k]; pushdown(k,l,r);int mid=(l+r)>>1,res; if(ps<=mid) res=Query(k<<1,l,mid,ps);if(mid<ps) res=Query(k<<1|1,mid+1,r,ps); pushup(k); return res; } int Qsum(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Sum[k]; pushdown(k,l,r);int mid=(l+r)>>1,res=0; if(x<=mid) res+=Qsum(k<<1,l,mid,x,y); if(mid<y) res+=Qsum(k<<1|1,mid+1,r,x,y); pushup(k); return res; } void QAns(int &val,int k,int l,int r,int &Ans){ if(Sum[k]<=val){val-=Sum[k];Ans+=(r-l+1);return;} pushdown(k,l,r);int mid=(l+r)>>1; if(Minn[k<<1]<=val) QAns(val,k<<1,l,mid,Ans); if(Minn[k<<1|1]<=val) QAns(val,k<<1|1,mid+1,r,Ans); pushup(k); return; } }S; signed main(){ //freopen("maker.in","r",stdin); N=read(),Q=read(); for(int i=1;i<=N;i++) A[i]=read();S.build(1,1,N); while(Q--){ int opt=read(),x=read(),y=read(); if(opt==1){ int l=1,r=x,res=-1; while(l<=r){ int mid=(l+r)>>1; if(S.Query(1,1,N,mid)<=y) res=mid,r=mid-1; else l=mid+1; } if(res!=-1) S.Modify(1,1,N,res,x,y); }else{ int Sum=0;if(x!=1) Sum=S.Qsum(1,1,N,1,x-1);y+=Sum; int Ans=0; S.QAns(y,1,1,N,Ans); printf("%lld\n",Ans-x+1); } }return 0; }/* 5 3 8 7 7 5 4 1 5 9 2 1 9 2 1 8 */
[x] CF1439B
代码调一年。。。 若 $\dfrac{k\times (k-1)}{2}>m$ 则肯定不会有 $k$ 个点的完全图。
而对于找一个点集使得每个点度数都 $\geq k$ 是一个经典问题,类似拓扑排序维护即可。
所以我们只需要在上述做法中找到度数为 $k-1$ 的点判断与其构成的点是否能为完全图。
时间复杂度 $O(m\sqrt{M})$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int d[MAXN],T,N,M,K,vis[MAXN]; vector<int> vec[MAXN],Ans; queue<int> que; int sta[MAXN]; bool check(int x){ Ans.clear();Ans.pb(x); int cnt=0; for(auto v:vec[x]){ if(d[v]>=K-1){ for(auto p:Ans) if(*lower_bound(vec[v].begin(),vec[v].end(),p)!=p||lower_bound(vec[v].begin(),vec[v].end(),p)==vec[v].end()){return 0;} Ans.pb(v); } }return Ans.size()==K; } int main(){ //freopen("maker.in","r",stdin); T=read();while(T--){ N=read(),M=read(),K=read(); bool ff=0; while(!que.empty()) que.pop(); for(int i=1;i<=N;i++) d[i]=0,vec[i].clear(),vis[i]=0; for(int i=1;i<=M;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);d[u]++,d[v]++;} for(int i=1;i<=N;i++) sort(vec[i].begin(),vec[i].end()); for(int i=1;i<=N;i++) if(d[i]<K){if(d[i]==K-1&&K*(K-1)<=2*M) ff|=check(i); if(ff) break; vis[i]=1,d[i]=0;que.push(i);} while(!que.empty()){ if(ff) break; int xx=que.front(); que.pop(); for(auto v:vec[xx]){ if(vis[v]) continue;d[v]--; if(d[v]==K-1&&K*(K-1)<=2*M) ff|=check(v); if(d[v]==K-1){vis[v]=1;d[v]=0;que.push(v);} if(ff) break; }if(ff) break; } if(ff){ printf("2\n"); for(auto v:Ans) printf("%d ",v);printf("\n"); }else{ sta[0]=0; for(int i=1;i<=N;i++) if(!vis[i]) sta[++sta[0]]=i; if(sta[0]){printf("1 %d\n",sta[0]); for(int i=1;i<=sta[0];i++) printf("%d ",sta[i]);printf("\n");} else printf("-1\n"); } }return 0; }/* 1 5 5 4 3 4 3 2 3 1 3 5 5 2 */
[x] CF1438E
考虑合法序列 $[l,r]$ ,可以发现在二进制分解中对于 $A_l$ 的最高位 $k$ 只会出现 $1$ 次,若出现超过 $1$ 次则 $\sum\geq 2^{k+1}$。
按照这个暴力即可,时间复杂度的计算可以考虑我们按照最高位会得到一个序列,对于 $i$ 的复杂度是 $nex_i-i$ ,则对于最高位是 $k$ 的复杂度和为 $O(n)$ ,则总时间复杂度为 $O(30n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #include<set> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; set<pii> s; int N,A[MAXN]; int main(){ N=read(); for(int i=1;i<=N;i++) A[i]=read(); for(int i=1;i<=N;i++){ int k; for(int j=1;j<=31;j++) if(A[i]<(1<<j)){k=(1<<j);break;} int sum=0; for(int j=i+1;j<=N-1;j++){ if(sum+A[j]>=k) break; sum+=A[j]; if(sum==(A[i]^A[j+1])) s.insert(mp(i,j+1)); } sum=0; for(int j=i-1;j>=2;j--){ if(sum+A[j]>=k) break; sum+=A[j]; if(sum==(A[j-1]^A[i])) s.insert(mp(j-1,i)); } } printf("%lu\n",s.size()); }
[] CF1437G
$cf$ 出板子题。。。 对串建 $AC$ 自动机后在 $fail$ 树上树剖维护。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #include<set> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; struct AC_String{ int ch[MAXN][28],tot=1,fail[MAXN]; queue<int> que; int ins(char *s){ int u=1,len=strlen(s); for(int i=0;i<len;i++){ if(!ch[u][s[i]-'a']) ch[u][s[i]-'a']=++tot; u=ch[u][s[i]-'a']; }return u; } void Get(){ for(int i=0;i<26;i++) ch[0][i]=1; que.push(1); while(!que.empty()){ int xx=que.front(); que.pop(); for(int i=0;i<26;i++){ if(!ch[xx][i]) ch[xx][i]=ch[fail[xx]][i]; else{ que.push(ch[xx][i]); fail[ch[xx][i]]=ch[fail[xx]][i]; } } }return; } }S; int N,Q,ps[MAXN],M; char str[MAXN]; multiset<int> s[MAXN]; vector<int> vec[MAXN]; struct Segment{ int Maxn[MAXN<<2]; void init(){memset(Maxn,-1,sizeof(Maxn)); return;} void Modify(int k,int l,int r,int ps,int w){ if(l==r){Maxn[k]=w;return;} int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); else Modify(k<<1|1,mid+1,r,ps,w); Maxn[k]=max(Maxn[k<<1],Maxn[k<<1|1]); return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Maxn[k]; int res=-1,mid=(l+r)>>1; if(x<=mid) res=max(res,Query(k<<1,l,mid,x,y)); if(mid<y) res=max(res,Query(k<<1|1,mid+1,r,x,y)); return res; } }T; int dfn[MAXN],rev[MAXN],fa[MAXN],top[MAXN],num,siz[MAXN],son[MAXN],W[MAXN]; void dfs(int u){ siz[u]=1; for(auto v:vec[u]){ fa[v]=u; dfs(v); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; }return; } void dfs1(int u){ if(son[u]){ dfn[son[u]]=++num,rev[num]=son[u],top[son[u]]=top[u]; dfs1(son[u]); } for(auto v:vec[u]){ if(v==son[u]) continue; dfn[v]=++num,rev[num]=v,top[v]=v; dfs1(v); }return; } int calc(int x){ int res=-1; while(x!=-1){ res=max(res,T.Query(1,1,M+1,dfn[top[x]],dfn[x])); x=fa[top[x]]; }return res; } int main(){ //freopen("1.in","r",stdin); N=read(),Q=read(); for(int i=1;i<=N;i++){ scanf("%s",str); int x=S.ins(str); ps[i]=x; } S.Get(); for(int i=1;i<=S.tot;i++) vec[S.fail[i]].pb(i); M=S.tot; for(int i=1;i<=N;i++) s[ps[i]].insert(0); fa[0]=-1;dfs(0); dfn[0]=num=1,rev[1]=0; dfs1(0);T.init(); for(int i=1;i<=M;i++) if(s[i].size()) T.Modify(1,1,M+1,dfn[i],0); while(Q--){ int opt=read(); if(opt==1){ int id=read(),x=ps[id],w=read(); s[x].erase(s[x].find(W[id])); W[id]=w; s[x].insert(w); //cerr<<"x:"<<x<<" "<<(*(--s[x].end()))<<endl; T.Modify(1,1,M+1,dfn[x],*(--s[x].end())); } if(opt==2){ int u=1,res=-1; scanf("%s",str); int len=strlen(str); for(int i=0;i<len;i++) u=S.ch[u][str[i]-'a'],res=max(res,calc(u)); printf("%d\n",res); } }return 0; }
[x] CF1437F
我们对让渔夫高兴的天去做 $dp$ ,设 $f_{i}$ 表示渔夫在第 $i$ 天会高兴的方案数。
处理 $lim_i=j$ 表示最大的 $j$ 使得 $2\times A_j\leq A_i$ ,则若渔夫在 $i$ 天高兴的上次为 $j$ 时我们就可以确定 $l_i-l_j-1$ 的相对位置。
$$f_i=\sum_j [2\times A_j\leq A_i]\cdot A(N-2-lim_j,lim_i-lim_j-1)$$
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define mod 998244353 #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=5e3+11; int N,A[MAXN],f[MAXN],Lim[MAXN],fac[MAXN],inv[MAXN],ifac[MAXN]; int calA(int a,int b){if(a<0||b<0||a<b) return 0;return fac[a]*ifac[a-b]%mod;} signed main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(); sort(A+1,A+N+1); fac[0]=fac[1]=inv[1]=ifac[0]=ifac[1]=1; for(int i=2;i<=N;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod; for(int i=1;i<=N;i++){ int l=0,r=i-1,res; while(l<=r){ int mid=(l+r)>>1; if(A[mid]*2<=A[i]) res=mid,l=mid+1; else r=mid-1; } Lim[i]=res; } f[0]=1,Lim[0]=-1; for(int i=1;i<=N;i++){ for(int j=0;j<=Lim[i];j++) f[i]+=f[j]*calA(N-2-Lim[j],Lim[i]-Lim[j]-1)%mod,f[i]%=mod; //cerr<<"i:"<<i<<" f:"<<f[i]<<endl; } if(A[N-1]*2<=A[N]) printf("%lld\n",f[N]); else printf("0\n"); return 0; }
[] CF1436E
考虑一个区间 $[l,r]$ 的 $mex$ 值为 $i$ 的条件 $p\in [l,r],A_p\neq i$ ,则我们可以枚举当前的 $mex$ 值,其中对应 $A$ 中的数为 ${p_1,p_2,...,p_k}$ ,则我们只需要判断 $(p_1,p_2),(p_2,p_3),...,(p_{k-1},p_k)$ 是否满足条件。
线段树维护最末出现该数位置即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; struct Segment{ int Minn[MAXN<<2],inf; void Modify(int k,int l,int r,int ps,int w){ if(l==r){Minn[k]=w;return;} int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); else Modify(k<<1|1,mid+1,r,ps,w); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]); return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Minn[k]; int mid=(l+r)>>1,res=INT_MAX; if(x<=mid) res=min(res,Query(k<<1,l,mid,x,y)); if(mid<y) res=min(res,Query(k<<1|1,mid+1,r,x,y)); return res; } }S; int N,A[MAXN],Ma[MAXN],lst[MAXN]; int main(){ //freopen("1.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(); for(int i=1;i<=N;i++) if(A[i]!=1) Ma[1]=1; for(int i=1;i<=N;i++){ if(A[i]>1&&S.Query(1,1,N,1,A[i]-1)>lst[A[i]]) Ma[A[i]]=1; lst[A[i]]=i,S.Modify(1,1,N,A[i],i); } for(int i=2;i<=N+1;i++) if(S.Query(1,1,N,1,i-1)>lst[i]) Ma[i]=1; for(int i=1;;i++) if(!Ma[i]){printf("%d\n",i);return 0;} }/* 5 1 4 3 1 2 */
[x] CF1427E
神仙构造。由于给定的是奇数最后想得到 $1$ 可以想到每次把最高为消掉,构造略。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int lowbit(int x){return x&-x;} vector<pair<int,pii> > vec; void calc(int &x){ int cc=0,s=x; while(s){cc++;s/=2ll;} s=x; for(int i=1;i<cc;i++){vec.pb(mp(1,mp(s,s)));s*=2ll;} int y=s; vec.pb(mp(0,mp(x,y))); int z=x^y; vec.pb(mp(1,mp(y,z))); int p=y+z; vec.pb(mp(0,mp(p,x))); int q=p^x; vec.pb(mp(1,mp(y,y))); int h=2*y; vec.pb(mp(0,mp(q,h))); int e=q^h; while(e<=z){ if(z&e) vec.pb(mp(0,mp(z,e))),z^=e; vec.pb(mp(1,mp(e,e))),e*=2ll; }x=z; return; } int X; signed main(){ //freopen("3.in","r",stdin); X=read(); while(X!=1) calc(X); printf("%lu\n",vec.size()); for(auto pp:vec){ if(pp.fi) printf("%lld + %lld\n",pp.se.fi,pp.se.se); else printf("%lld ^ %lld\n",pp.se.fi,pp.se.se); } return 0; }
[] CF1425B
大力分类讨论。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define mod 1000000007 #define LL long long using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; vector<int> vec[MAXN]; int f[MAXN][MAXN<<1],g[MAXN][MAXN<<1],Sf[MAXN][MAXN<<1],Sg[MAXN][MAXN<<1],N,M,tot,cnt,A[MAXN],vis[MAXN],F[MAXN][MAXN],G[MAXN][MAXN]; LL Ans; void dfs(int u){++tot; vis[u]=1;for(auto v:vec[u]) if(!vis[v]) dfs(v);return; } int Query(int l,int r,int id){l=max(l,-N),r=min(r,N); l+=N,r+=N; if(l>r) return 0; if(!l) return Sg[id][r]; return (Sg[id][r]-Sg[id][l-1]+mod)%mod;} signed main(){ //freopen("maker.in","r",stdin); N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} vis[1]=1;for(auto v:vec[1]) if(!vis[v]) tot=0,dfs(v),A[++cnt]=tot+1; sort(A+1,A+cnt+1); //for(int i=1;i<=cnt;i++) printf("%lld ",A[i]);printf("\n"); f[0][0+N]=1,g[cnt+1][0+N]=1; for(int i=1;i<=cnt;i++){ for(int j=-N;j<=N;j++){ f[i][j+N]+=f[i-1][j+N],f[i][j+N]%=mod; if(j+A[i]<=N) f[i][j+A[i]+N]+=f[i-1][j+N],f[i][j+A[i]+N]%=mod; if(j-A[i]>=-N) f[i][j-A[i]+N]+=f[i-1][j+N],f[i][j-A[i]+N]%=mod; } } for(int i=cnt;i>=1;i--){ for(int j=-N;j<=N;j++){ g[i][j+N]+=g[i+1][j+N],g[i][j+N]%=mod; if(j+A[i]<=N) g[i][j+A[i]+N]+=g[i+1][j+N],g[i][j+A[i]+N]%=mod; if(j-A[i]>=-N) g[i][j-A[i]+N]+=g[i+1][j+N],g[i][j-A[i]+N]%=mod; } } for(int i=0;i<=cnt+1;i++) for(int j=0;j<=2*N;j++) Sf[i][j]=(j==1?0:Sf[i][j-1])+f[i][j],Sg[i][j]=(j==1?0:Sg[i][j-1])+g[i][j],Sf[i][j]%=mod,Sg[i][j]%=mod; //opt1 meet in i circle for(int i=1;i<=cnt;i++){ int L=2-A[i],R=A[i]-2; if(L>R) swap(L,R); for(int j=-N;j<=N;j++){ Ans+=(LL)f[i-1][j+N]*Query(L-j,R-j,i+1)%mod,Ans%=mod; //cerr<<"i:"<<i<<" j:"<<j<<" "<<f[i-1][j+N]*Query(L-j,R-j,i+1)%mod<<endl; } }Ans=Ans*2ll%mod; if(M&1){ //opt2 run and someone in 1 and someone in circle and distance is 1 F[0][0]=1,G[cnt+1][0]=1; for(int i=0;i<cnt;i++){ for(int j=0;j<=N;j++){ if(!F[i][j]) continue; F[i+1][j+A[i+1]]+=F[i][j],F[i+1][j+A[i+1]]%=mod; F[i+1][j]+=F[i][j],F[i+1][j]%=mod; } } for(int i=cnt+1;i>1;i--){ for(int j=0;j<=N;j++){ if(!G[i][j]) continue; G[i-1][j+A[i-1]]+=G[i][j],G[i-1][j+A[i-1]]%=mod; G[i-1][j]+=G[i][j],G[i-1][j]%=mod; } } //cerr<<F[1][2]<<" "<<A[1]<<endl; for(int i=1;i<=cnt;i++){ //assume A got 1 and B got i int sum=(M-1)/2; for(int j=0;j<=sum;j++){ Ans+=(LL)4ll*F[i-1][j]*G[i+1][sum-j]%mod,Ans%=mod; } } }else{ //opt3 run all and point 1 memset(f,0,sizeof(f)); f[0][0+N]=1; for(int i=1;i<=cnt;i++){ for(int j=-N;j<=N;j++){ if(j+A[i]<=N) f[i][j+A[i]+N]+=f[i-1][j+N],f[i][j+A[i]+N]%=mod; if(j-A[i]>=-N) f[i][j-A[i]+N]+=f[i-1][j+N],f[i][j-A[i]+N]%=mod; } } Ans+=f[cnt][0+N];Ans%=mod; } printf("%lld\n",Ans); return 0; }/* 4 5 2 1 1 2 4 3 4 1 1 3 */
[] CF1423L
折半搜索,$bitset$ 优化。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<map> #include<unordered_map> #include<bitset> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define LL long long #define mod 998244353 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+1; bitset<MAXN> A[MAXN],now,res; int N,S,Q; unordered_map< bitset<MAXN> ,int> M; vector< pair<bitset<MAXN>,int> > vec; int main(){ //freopen("8.in","r",stdin); N=read(),S=read(),Q=read(); int Lim=S-S/3; for(int i=1;i<=S;i++){int siz=read(); for(int j=1;j<=siz;j++) A[i][read()]=1;} for(int i=0;i<(1<<Lim);i++){ now.reset(); int tot=0; for(int j=1;j<=Lim;j++) if(i&(1<<(j-1))) tot++,now^=A[j]; if(!M.count(now)) M[now]=tot; else M[now]=min(M[now],tot); } for(int i=0;i<(1<<(S-Lim));i++){ now.reset(); int tot=0; for(int j=Lim+1;j<=S;j++) if(i&(1<<(j-Lim-1))) tot++,now^=A[j]; vec.pb(mp(now,tot)); } for(int i=1;i<=Q;i++){ res.reset(); int siz=read(); for(int j=1;j<=siz;j++) res[read()]=1; int Minn=INT_MAX; for(auto p:vec) if(M.count(p.fi^res)) Minn=min(Minn,p.se+M[p.fi^res]); if(Minn!=INT_MAX) printf("%d\n",Minn); else printf("-1\n"); }return 0; }/* 1 10 8 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 0 1 1 */
[x] CF1423J
神仙 $2400$ 的题。
我们将式子写成 $\sum x_i\cdot 2^i$ 的形式,由于 $x_i\in[0,7]$,可以想到将式子转换成 $\sum x_i\cdot 8^{i}+2\sum x_i\cdot 8^{i+1}+4\sum x_i\cdot 8^{i+2}$ 而对与每个数都可以与一种八进制数对应!!!
则答案可以写成求 $x+2y+4z=W$ 的组数,这个就变得简单了。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define mod 1000000007 #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int T; signed main(){ T=read(); while(T--){ int x=read(),res=0; int be=x/2-2*(x/4)+1,end=x/2+1,cnt=(x/4)+1; printf("%lld\n",((be+end)%mod*(cnt%mod)%mod)%mod*((mod+1)/2)%mod); //for(int i=0;i<=(x/4);i++) res+=(x/2-2*i+1),res%=mod; //printf("%lld\n",res); }return 0; }
[] CF1423H
线段树分治模板题。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<stack> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e6+11; int f[MAXN],siz[MAXN],N,M,T[MAXN],K; stack<pii> sta; int find(int x){return f[x]==x?x:find(f[x]);} void merge(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return; if(siz[t1]<siz[t2]) swap(t1,t2); f[t2]=t1,siz[t1]+=siz[t2];sta.push(mp(t1,t2)); } struct Query{int opt,x,y,now;} Que[MAXN]; struct Segment{ vector<pii> vec[MAXN<<2]; void ins(int k,int l,int r,int x,int y,pii p){ if(x<=l&&r<=y){vec[k].pb(p);return;} int mid=(l+r)>>1; if(x<=mid) ins(k<<1,l,mid,x,y,p); if(mid<y) ins(k<<1|1,mid+1,r,x,y,p); return; } void Query(int k,int l,int r){ if(l==r){ int siz1=sta.size(); for(auto pp:vec[k]) merge(pp.fi,pp.se); if(Que[l].opt==2) printf("%d\n",siz[find(Que[l].x)]); while(sta.size()!=siz1){int t1=sta.top().fi,t2=sta.top().se;sta.pop();f[t2]=t2;siz[t1]-=siz[t2];} return; } int siz1=sta.size(); for(auto pp:vec[k]) merge(pp.fi,pp.se); int mid=(l+r)>>1; Query(k<<1,l,mid),Query(k<<1|1,mid+1,r); while(sta.size()!=siz1){int t1=sta.top().fi,t2=sta.top().se;sta.pop();f[t2]=t2;siz[t1]-=siz[t2];} return; } }S; int main(){ //freopen("10.in","r",stdin); N=read(),M=read(),K=read(); for(int i=1;i<=N;i++) f[i]=i,siz[i]=1; for(int i=1;i<=M;i++){ int opt=read(); Que[i].opt=opt; if(opt==1){Que[i].x=read(),Que[i].y=read();Que[i].now=T[0];} else if(opt==2) Que[i].x=read(); else T[++T[0]]=i; } for(int i=1;i<=M;i++){ if(Que[i].opt==1){ int L=i,R=(T[Que[i].now+K]?T[Que[i].now+K]:M); //cerr<<"L:"<<L<<" R:"<<R<<" u:"<<Que[i].x<<" v:"<<Que[i].y<<endl; S.ins(1,1,M,L,R,mp(Que[i].x,Que[i].y)); } } S.Query(1,1,M); }
[x] CF1408E
若在 $i$ 中存在颜色 $j$ 便连上 $(i,j+N,A_i+B_j)$ 的边,跑最大生成树即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; int N,M,Ans,tot; struct Union{ int f[MAXN]; void init(){for(int i=1;i<=N+M;i++) f[i]=i;return;} int find(int x){return f[x]==x?x:f[x]=find(f[x]);} }U; struct Edge{int u,v,w;} E[MAXN]; bool cmp(Edge x1,Edge x2){return x1.w>x2.w;} int A[MAXN],B[MAXN]; signed main(){ //freopen("7.in","r",stdin); N=read(),M=read(); U.init(); for(int i=1;i<=N;i++) A[i]=read(); for(int i=1;i<=M;i++) B[i]=read(); for(int i=1;i<=N;i++){ int siz=read(); for(int j=1;j<=siz;j++) {int x=read(); E[++tot].u=i,E[tot].v=x+N,E[tot].w=A[i]+B[x];Ans+=E[tot].w;} } sort(E+1,E+tot+1,cmp); for(int i=1;i<=tot;i++){ int u=U.find(E[i].u),v=U.find(E[i].v); if(u==v) continue; U.f[v]=u;Ans-=E[i].w; }printf("%lld\n",Ans); return 0; }
[] CF1407E
由于正图要考虑 $u$ 点颜色对后面点的影响,考虑建立反图跑最短路,设 $f_{u,0/1}$ 表示当前在 $u$ 点,颜色为 $0/1$ 的最长距离。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=500011; vector<pii> vec[MAXN]; int N,M,dis[MAXN][2],vis[MAXN],inf; queue<int> que; void spfa(){ memset(dis,127/3,sizeof(dis)); inf=dis[0][0]; dis[N][0]=dis[N][1]=0; que.push(N); while(!que.empty()){ int xx=que.front(); que.pop(); vis[xx]=0; int W=max(dis[xx][0],dis[xx][1]); for(auto pp:vec[xx]){ int v=pp.fi,c=pp.se;if(dis[v][c]>W+1){dis[v][c]=W+1; if(!vis[v]) que.push(v);} } }return; } int main(){ N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read(),w=read();vec[v].pb(mp(u,w));} spfa(); int Ans=max(dis[1][0],dis[1][1]); if(Ans>=inf) printf("-1\n"); else printf("%d\n",Ans); for(int i=1;i<=N;i++) printf(dis[i][0]>dis[i][1]?"0":"1");printf("\n"); return 0; }
[x] CF1406E
神仙交互题。 详情请见 link 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,pri[MAXN],v[MAXN]; bool is(int x){return v[x]==x;} int QueryA(int x){printf("A %lld\n",x);fflush(stdout);int xx;cin>>xx;return xx;} int QueryB(int x){printf("B %lld\n",x);fflush(stdout);int xx;cin>>xx;return xx;} void tell(int x){printf("C %lld\n",x);fflush(stdout); exit(0);} vector<int> vec; signed main(){ N=read(); if(N==1) tell(1); for(int i=2;i<=N;i++){ if(!v[i]){v[i]=i,pri[++pri[0]]=i;} for(int j=1;pri[j]<=N/i&&j<=pri[0];j++){v[i*pri[j]]=pri[j];if(!(i%pri[j])) break;} } int Ans=1,Lim=1; for(int i=2;i*i<=N;i++){ if(is(i)){ int p=QueryB(i); for(int j=i;Ans*i<=N;j*=i){ if(!QueryB(j)) break; Ans*=i; }Lim=i; } } int S=90,tot=0; //cerr<<"Ans:"<<Ans<<endl;; int real=QueryA(1),num=0; if(Ans==1){ for(int i=Lim+1;i<=N;i++){ if(is(i)){ tot++; QueryB(i); vec.pb(i); ++num; if(tot==S){ if(QueryA(1)!=real-num) for(auto v:vec) if(QueryA(v)) tell(v); tot=0;vec.clear(); } } } if(QueryA(1)!=real-num) for(auto v:vec) if(QueryA(v)) tell(v); tell(1); return 0; } for(int i=Lim+1;i<=N;i++){ if(is(i)&&Ans*i<=N) if(QueryA(Ans*i)!=0) tell(Ans*i); }tell(Ans); return 0; }
[] CF1401F
首先肯定会想到线段树,则 $2,3$ 操作分别表示让第 $k$ 层的左右儿子结点交换,与让 $k$ 至 $n$ 层的左右结点均交换,对每个点打标记记录当前已经完成 $2/3$ 操作次数。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=262145; int N,Q,A[MAXN],M,Sum1[MAXN],Sum2[MAXN],rt; struct Segment{ int dep[MAXN<<2],tag1[MAXN<<2],tag2[MAXN<<2],ls[MAXN<<2],rs[MAXN<<2],tot,Sum[MAXN<<2],res1[MAXN<<2],res2[MAXN<<2]; void build(int &k,int d,int l,int r){ k=++tot;dep[k]=d;if(l==r){Sum[k]=A[l];return;} int mid=(l+r)>>1; build(ls[k],d-1,l,mid),build(rs[k],d-1,mid+1,r); Sum[k]=Sum[ls[k]]+Sum[rs[k]];return; } void pushdown(int k){ //swap int x=Sum2[dep[k]]-res2[k]; res2[k]+=x; if(x&1) swap(ls[k],rs[k]); x=Sum1[dep[k]]-res1[k]; res1[k]+=x; x+=tag1[k]; if(x&1){ swap(ls[k],rs[k]); tag1[ls[k]]++,tag1[rs[k]]++; } tag1[k]=0;return; } void Modify(int k,int l,int r,int ps,int w){ if(l==r){Sum[k]=w;return;} pushdown(k); int mid=(l+r)>>1; if(ps<=mid) Modify(ls[k],l,mid,ps,w); if(mid<ps) Modify(rs[k],mid+1,r,ps,w); Sum[k]=Sum[ls[k]]+Sum[rs[k]]; return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Sum[k]; pushdown(k); int mid=(l+r)>>1,res=0; if(x<=mid) res+=Query(ls[k],l,mid,x,y); if(mid<y) res+=Query(rs[k],mid+1,r,x,y); return res; } }S; void print(){ for(int i=1;i<=N;i++) printf("%lld ",S.Query(rt,1,N,i,i));printf("\n"); return; } signed main(){ //freopen("1.in","r",stdin); N=(1<<(M=read())),Q=read(); for(int i=1;i<=N;i++) A[i]=read(); S.build(rt,M,1,N); while(Q--){ int opt=read(); //print(); if(opt==1){int x=read(),w=read(); S.Modify(rt,1,N,x,w);} if(opt==2){int k=read(); Sum1[k]++;continue;} if(opt==3){int k=read(); Sum2[k+1]++;continue;} if(opt==4){int l=read(),r=read(); printf("%lld\n",S.Query(rt,1,N,l,r));} } /*print();*/return 0; }/* 3 3 7 0 8 8 7 1 5 2 2 1 3 2 2 3 */
[] CF1401E
猜发结论:答案=横竖的交点+可以将图一份为二的横纵线段个数+1。
证明可以考虑我们现将横线段插进去,当前答案应为 1+可以将图一份为 $2$ 的横线段个数,而对于每个插入的纵线段,若与横线段有交点则答案肯定会 $+1$,可以一份为 $2$ 答案也会 $+1$ 。得证。
故我们只需要求交点即可,可以发现这是个二维数点,主席树维护。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define LL long long using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; const int MAXM=2e6+11; int N,M,tot,rt[MAXN*11],Lim=1e6; vector<int> vec0[MAXM],vec1[MAXM],vec2[MAXM],vec3[MAXM]; LL Ans; struct Segment{ int Ans[MAXN*31],ls[MAXN*31],rs[MAXN*31]; void clear(){memset(Ans,0,sizeof(Ans));tot=0;memset(rt,0,sizeof(rt));} void build(int &k,int l,int r){k=++tot;if(l==r) return; int mid=(l+r)>>1; build(ls[k],l,mid); build(rs[k],mid+1,r);} void Add(int p,int &q,int l,int r,int ps){ q=++tot; ls[q]=ls[p],rs[q]=rs[p],Ans[q]=Ans[p]+1; if(l==r) return; int mid=(l+r)>>1; if(ps<=mid) Add(ls[p],ls[q],l,mid,ps); if(mid<ps) Add(rs[p],rs[q],mid+1,r,ps); } int Query(int k,int l,int r,int x,int y){ if(!k) return 0;if(x<=l&&r<=y) return Ans[k]; int mid=(l+r)>>1,res=0; if(x<=mid) res+=Query(ls[k],l,mid,x,y); if(mid<y) res+=Query(rs[k],mid+1,r,x,y); return res; } }S; int main(){ //freopen("3.in","r",stdin); N=read(),M=read(); for(int i=1;i<=N;i++){ int P=read(),X=read(),Y=read(); if(!X) vec0[P].pb(Y-X); else vec1[P].pb(Y-X); if(Y-X==Lim) Ans++; } for(int i=1;i<=M;i++){ int P=read(),X=read(),Y=read(); if(!X) vec2[P].pb(Y-X); else vec3[P].pb(Y-X); if(Y-X==Lim) Ans++; } //=====solve 0 S.build(rt[0],0,Lim); for(int i=1;i<=Lim;i++){rt[i]=rt[i-1]; int p=0;for(auto v:vec0[i]) S.Add(rt[i],p,0,Lim,v),rt[i]=p;} for(int i=1;i<=Lim;i++){ for(auto v:vec2[i]){ Ans+=S.Query(rt[v],0,Lim,i,Lim); } } //calc3 for(int i=1;i<=Lim;i++){ for(auto v:vec3[i]){ Ans+=S.Query(rt[Lim],0,Lim,i,Lim); if(v!=Lim) Ans-=S.Query(rt[Lim-v-1],0,Lim,i,Lim); } } //=====solve 1 S.clear(); S.build(rt[0],0,Lim); for(int i=1;i<=Lim;i++){rt[i]=rt[i-1]; for(auto v:vec1[i]) S.Add(rt[i],rt[i],0,Lim,v);} //calc2 for(int i=1;i<=Lim;i++){ for(auto v:vec2[i]) Ans+=S.Query(rt[v],0,Lim,Lim-i,Lim); } //calc3 for(int i=1;i<=Lim;i++){ for(auto v:vec3[i]){ Ans+=S.Query(rt[Lim],0,Lim,Lim-i,Lim); if(v!=Lim) Ans-=S.Query(rt[Lim-v-1],0,Lim,Lim-i,Lim); } } printf("%lld\n",Ans+1); return 0; }/* 1 3 500000 0 1000000 1 0 1000000 2 0 1000000 3 0 499999 */
[x] CF1400G
题读错了,导致不会。。。
由于 $m\leq 20$ 则优先考虑容斥,设 $f(k)$ 表示钦定 $k$ 个点对必选的方案数,假设这 $k$ 个点对的 $[l,r]$ 交集为 $[L,R]$ ,则答案为 $\sum_{i=L}^R \dbinom{s_i-p}{i-p}$ , $p$ 表示实际点数。
而枚举 $k$ 时间复杂度 $2^k\times n$ 通过不了此题,但是我们发现 $p\leq 40$ ,前缀和维护上式即可查询做到 $O(1)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define mod 998244353 #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; const int MAXM=21; int S[MAXN],N,L[MAXN],R[MAXN],A[MAXM],B[MAXM],Sum[MAXN][MAXM<<1],M; int fac[MAXN],ifac[MAXN],inv[MAXN],Num[MAXN],Ans; int C(int a,int b){if(a<b||a<0||b<0) return 0; return fac[a]*ifac[b]%mod*ifac[a-b]%mod;} int lowbit(int x){return x&-x;}int cont(int x){int c=0;while(x){c++;x-=lowbit(x);}; return c;} signed main(){ //freopen("6.in","r",stdin); N=read(),M=read(); for(int i=1;i<=N;i++) L[i]=read(),R[i]=read(),S[L[i]]++,S[R[i]+1]--; for(int i=1;i<=M;i++) A[i]=read(),B[i]=read(); for(int i=1;i<=N;i++) S[i]+=S[i-1]; fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1; for(int i=2;i<=N;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod; for(int i=1;i<=N;i++){ for(int j=0;j<=2*M;j++){ Sum[i][j]=Sum[i-1][j]+C(S[i]-j,i-j);Sum[i][j]%=mod; } } memset(Num,-1,sizeof(Num)); for(int i=0;i<(1<<M);i++){ int l=1,r=N; int cc=0; for(int j=1;j<=M;j++) if(i&(1<<(j-1))){ if(Num[A[j]]!=i) Num[A[j]]=i,cc++; if(Num[B[j]]!=i) Num[B[j]]=i,cc++; l=max(l,max(L[A[j]],L[B[j]])),r=min(r,min(R[A[j]],R[B[j]])); } if(l>r) continue; int pw=((cont(i)&1)?-1:1),res=(Sum[r][cc]-Sum[l-1][cc]+mod)%mod; Ans+=pw*res; Ans=((Ans%mod)+mod)%mod; } printf("%lld\n",Ans); return 0;
[] CF1398G
被埋伏了。 由于倍数关系可以通过调和级数递推则我们只考虑是否在 $A$ 中存在两个元素 $x,y$ 使得 $y-x=k$ 。
可以对 $A$ 值域作多项式求 $NTT$ 即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define mod 998244353 #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; const int MAXM=21; int S[MAXN],N,L[MAXN],R[MAXN],A[MAXM],B[MAXM],Sum[MAXN][MAXM<<1],M; int fac[MAXN],ifac[MAXN],inv[MAXN],Num[MAXN],Ans; int C(int a,int b){if(a<b||a<0||b<0) return 0; return fac[a]*ifac[b]%mod*ifac[a-b]%mod;} int lowbit(int x){return x&-x;}int cont(int x){int c=0;while(x){c++;x-=lowbit(x);}; return c;} signed main(){ //freopen("6.in","r",stdin); N=read(),M=read(); for(int i=1;i<=N;i++) L[i]=read(),R[i]=read(),S[L[i]]++,S[R[i]+1]--; for(int i=1;i<=M;i++) A[i]=read(),B[i]=read(); for(int i=1;i<=N;i++) S[i]+=S[i-1]; fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1; for(int i=2;i<=N;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod; for(int i=1;i<=N;i++){ for(int j=0;j<=2*M;j++){ Sum[i][j]=Sum[i-1][j]+C(S[i]-j,i-j);Sum[i][j]%=mod; } } memset(Num,-1,sizeof(Num)); for(int i=0;i<(1<<M);i++){ int l=1,r=N; int cc=0; for(int j=1;j<=M;j++) if(i&(1<<(j-1))){ if(Num[A[j]]!=i) Num[A[j]]=i,cc++; if(Num[B[j]]!=i) Num[B[j]]=i,cc++; l=max(l,max(L[A[j]],L[B[j]])),r=min(r,min(R[A[j]],R[B[j]])); } if(l>r) continue; int pw=((cont(i)&1)?-1:1),res=(Sum[r][cc]-Sum[l-1][cc]+mod)%mod; Ans+=pw*res; Ans=((Ans%mod)+mod)%mod; } printf("%lld\n",Ans); return 0; }
[] CF1398F
暴力过题了???
由于枚举答案是一个调和级数则我们只要快速找到段的位置即可。
可以发现我们每次优先选取可能的是最优的,则处理每个点为开头能往右最多走多少。
而暴力的做法是对这个建线段树二分可能位置,时间复杂度 $O(n\ln n\log n)$ 。
只要特判一下 $20th\space case$ 全是 $0$ 或 $1$ 即可过题。
其实按理说也能过的!
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; int N,S1[MAXN],S2[MAXN],S3[MAXN],len[MAXN]; char str[MAXN]; int Q1(int l,int r){return S1[r]-S1[l-1];} int Q2(int l,int r){return S2[r]-S2[l-1];} vector<int> vec[MAXN]; int tmp=-1; struct Segment{ int Ans[MAXN<<2]; void Modify(int k,int l,int r,int ps,int w){ Ans[k]+=w; if(l==r) return; int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); else Modify(k<<1|1,mid+1,r,ps,w); } void Find(int k,int l,int r){ if(l==r){tmp=l;return;} int mid=(l+r)>>1; if(Ans[k<<1]) Find(k<<1,l,mid); else Find(k<<1|1,mid+1,r); return; } void Query(int k,int l,int r,int x,int y){ if(!Ans[k]) return; if(tmp!=-1) return; if(x<=l&&r<=y){Find(k,l,r);return;} int mid=(l+r)>>1; if(x<=mid) Query(k<<1,l,mid,x,y); if(mid<y) Query(k<<1|1,mid+1,r,x,y); } }S; int main(){ //freopen("1.in","r",stdin); N=read(); scanf("%s",str+1); for(int i=1;i<=N;i++){ S1[i]=S1[i-1],S2[i]=S2[i-1],S3[i]=S3[i-1]; if(str[i]=='0') S1[i]++; if(str[i]=='1') S2[i]++; if(str[i]=='?') S3[i]++; } if(!S1[N]||!S2[N]){ for(int i=1;i<=N;i++) printf("%d ",N/i);printf("\n"); return 0; } for(int i=1;i<=N;i++){ int l=i,r=N,res=-1; while(l<=r){ int mid=(l+r)>>1; if(!Q1(i,mid)||!Q2(i,mid)) res=mid,l=mid+1; else r=mid-1; } len[i]=res-i+1; vec[len[i]].pb(i); S.Modify(1,1,N,i,1); } //for(int i=1;i<=N;i++) printf("%d ",len[i]);printf("\n"); for(int i=1;i<=N;i++){ for(auto v:vec[i-1]) S.Modify(1,1,N,v,-1); int Ans=0,ps=1; while(ps<=N){ tmp=-1; S.Query(1,1,N,ps,N); if(tmp==-1) break; ps=tmp+i; Ans++; } printf("%d ",Ans); }printf("\n"); return 0; }/* 6 11?000 */
[x] CF1394C
看到最大值最小优先考虑二分,设当前二分值为 $k$。
可以发现我们不需要知道答案串每个 $B,N$ 的位置,只需要知道 $B,N$ 的个数,因为对于两个串 $i,j$ 的编辑距离可以写成。
$$\max\{|N_i-N_j|,|B_i-B_j|\},s(N_i-N_j)=s(B_i-B_j)\\
|N_i-N_j|+|B_i-B_j|,s(N_i-N_j)\neq s(B_i-B_j)$$
其中 $N_i$ 表示 $i$ 串的 $N$ 的个数,同理可得 $B_i$ 。而对于这个二分还是不太好做,是一个对等边直角三角形的点覆盖问题。
考虑继续简化题意,设满足题意的 $x=N,y=B$ ,则
$$N_i-k\leq x\leq N_i+k\\
B_i-k\leq y\leq B_i+k\\
|(x-y)-(N_i-B_i)|\leq k$$
所以我们可以知道 $x,y,x-y$ 的范围,判断是否存在 $x,y$ 符合条件即可。挺离谱的。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=5e5+11; char str[MAXN]; int X[MAXN],Y[MAXN],N,cntX,cntY; bool check(int K,int opt){ int LX=0,RX=INT_MAX,LY=0,RY=INT_MAX,L=INT_MIN,R=INT_MAX; for(int i=1;i<=N;i++) LX=max(LX,X[i]-K),RX=min(RX,X[i]+K),LY=max(LY,Y[i]-K),RY=min(RY,Y[i]+K),L=max(L,-K+(X[i]-Y[i])),R=min(R,K+(X[i]-Y[i])); if(LX>RX||LY>RY||L>R) return 0; int LL=LX-RY,RR=RX-LY; if(max(LL,L)>min(RR,R)) return 0; if(opt){ for(int i=LY;i<=RY;i++){ int ll=i+L,rr=i+R; if(max(ll,LX)<=min(rr,RX)){ if(i||max(ll,LX)){cntY=i,cntX=max(ll,LX);return 1;} if(max(ll,LX)<min(rr,RX)){ cntY=i,cntX=max(ll,LX)+1;return 1; } } } } return 1; } int main(){ //freopen("1.in","r",stdin); N=read(); for(int i=1;i<=N;i++){ scanf("%s",str+1);int len=strlen(str+1); for(int j=1;j<=len;j++) if(str[j]=='N') X[i]++;else Y[i]++; } int l=0,r=500000,res=-1; while(l<=r){ int mid=(l+r)>>1; if(check(mid,0)) res=mid,r=mid-1; else l=mid+1; }printf("%d\n",res); check(res,1); for(int i=1;i<=cntX;i++) printf("N"); for(int i=1;i<=cntY;i++) printf("B");printf("\n"); return 0; }
[x] CF1392F
结论题,满足 $A_i=A_{i+1}$ 的只能有 $1$ 个。证明考虑每次调整若出现相等那么必定在其右端又能调整使得相等的只会有 $1$ 个。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; int N,s,A[MAXN],res; signed main(){ N=read(); for(int i=1;i<=N;i++) s+=read(); for(int i=0;i<N;i++) A[i+1]=i,res+=i; int ff=(s-res)/N; for(int i=1;i<=N;i++) A[i]+=ff,res+=ff; for(int i=1;i<=N;i++) if(res<s) A[i]++,res++; for(int i=1;i<=N;i++) printf("%lld ",A[i]);printf("\n"); return 0; }
[x] CF1391E
神仙构造题。 我也不知道为什么可以想到 $dfs$ 树。。。
对图建 $dfs$ 树,若深度有 $\geq ⌈\dfrac{n}{2}⌉$ 的直接输出路径即可,则现在这棵树深度一定小于 $⌈\dfrac{n}{2}⌉$ 。
对于 $dfs$ 树有个性质为在原图上存在 $(u,v)\in E$,则在 $dfs$ 树上 $u,v$ 只能是祖先-子孙关系,证明十分简单。
我们只需要把树上所有兄弟点对输出即可,因为这样最多也只会产生两条边,而每个高度最多只会剩 $1$ 个,符合题意。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=5e5+11; int T,N,M,dep[MAXN],fa[MAXN]; vector<int> vec[MAXN],Ans1; vector<pii> Ans2; void dfs(int u,int fath){ dep[u]=dep[fath]+1,fa[u]=fath; for(auto v:vec[u]) if(!dep[v]) dfs(v,u); return; } int main(){ //freopen("1.in","r",stdin); T=read();while(T--){ N=read(),M=read(); for(int i=1;i<=N;i++) vec[i].clear(),dep[i]=0; Ans1.clear(),Ans2.clear(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} dfs(1,0); int Maxn=0; for(int i=1;i<=N;i++) Maxn=max(Maxn,dep[i]); for(int i=1;i<=N;i++) vec[i].clear(); if(Maxn>=(N+1)/2){ printf("PATH\n"); int u=-1;for(int i=1;i<=N;i++) if(dep[i]>=(N+1)/2) u=i; while(u){Ans1.pb(u);u=fa[u];} printf("%lu\n",Ans1.size()); for(auto v:Ans1) printf("%d ",v);printf("\n"); }else{ printf("PAIRING\n"); for(int i=1;i<=N;i++) vec[dep[i]].pb(i); for(int i=1;i<=(N+1)/2;i++){ int siz=vec[i].size(); for(int j=0;j+1<siz;j+=2) Ans2.pb(mp(vec[i][j],vec[i][j+1])); } printf("%lu\n",Ans2.size()); for(auto v:Ans2) printf("%d %d\n",v.fi,v.se); } }return 0; }
[x] CF1387B2
对经过的边算贡献,可以发现答案的上界为 $\sum min(siz_i,N-siz_i)$ ,而这个上界正好能取到!
当取到上界时的要求为对于 $i$ 的子树下任意一点都有跑到 $i$ 的上面,假设 $siz_i<N-siz_i$ 。
而我们可以通过将重心为根建树,我们保证了每个点都符合 $siz_i<N-siz_i$ 的条件,且我们只需要让每个点跑到其他子树即可。
构造可以将所有数按照子树排在一起,每次互相匹配 $i,i+\dfrac{n}{2}$ 即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,rt,siz[MAXN],Ans; vector<int> vec[MAXN]; void dfs(int u,int fath,int opt){ int Maxn=0; siz[u]=1; for(auto v:vec[u]){ if(v==fath) continue; dfs(v,u,opt);siz[u]+=siz[v]; Maxn=max(Maxn,siz[v]); } Maxn=max(Maxn,N-siz[u]); if(!opt) if(!Ans||Maxn<Ans) Ans=Maxn,rt=u; return; } int sta[MAXN],nex[MAXN]; void dfs1(int u,int fath){sta[++sta[0]]=u;for(auto v:vec[u]) if(v!=fath) dfs1(v,u);} signed main(){ //freopen("6.in","r",stdin); N=read(); for(int i=1;i<N;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} dfs(1,0,0);dfs(rt,0,1); int Ans=0; for(int i=1;i<=N;i++) Ans+=2*min(siz[i],N-siz[i]); printf("%lld\n",Ans); for(auto u:vec[rt]) dfs1(u,rt); sta[++sta[0]]=rt; int e=(N/2); //for(int i=1;i<=N;i++) printf("%d ",sta[i]);printf("\n"); for(int i=1;i<=e;i++){int u=sta[i],v=sta[i+e];nex[u]=v,nex[v]=u;} if(N&1){ int u=sta[1],v=sta[1+e],x=rt; nex[u]=v,nex[v]=rt,nex[rt]=u; } for(int i=1;i<=N;i++) printf("%lld ",nex[i]);printf("\n"); return 0; }
[] CF1381C
可以发现对于 $x$ 我们只需要将出现次数多的给 $x$ 即可。而现在的问题是如何确立 $y-x$ 个数,按上题的做法构造即可?
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,T,A[MAXN],X,Y,Ans[MAXN]; priority_queue<pii> que; vector<int> vec[MAXN]; int main(){ //freopen("2.in","r",stdin); T=read();for(int cas=1;cas<=T;cas++){ N=read(),X=read(),Y=read(); for(int i=1;i<=N+1;i++) vec[i].clear(),Ans[i]=0; while(!que.empty()) que.pop(); for(int i=1;i<=N;i++) A[i]=read(),vec[A[i]].pb(i); for(int i=1;i<=N+1;i++) que.push(mp(vec[i].size(),i)); int t=-1; for(int i=1;i<=N+1;i++) if(!vec[i].size()){t=i;break;} for(int i=1;i<=X;i++){ int col=que.top().se,u=vec[col][vec[col].size()-1]; vec[col].pop_back(); que.pop(); Ans[u]=u;que.push(mp(vec[col].size(),col)); } bool ff=1; for(int i=1;i*2<=Y-X;i++){ int col1=que.top().se; que.pop(); int col2=que.top().se; que.pop(); if(!vec[col1].size()||!vec[col2].size()) {ff=0;printf("NO\n");break;} int u=vec[col1][vec[col1].size()-1],v=vec[col2][vec[col2].size()-1]; vec[col1].pop_back(),vec[col2].pop_back(); que.push(mp(vec[col1].size(),col1)),que.push(mp(vec[col2].size(),col2)); Ans[u]=v,Ans[v]=u; } if(!ff) continue; if((Y-X)&1){ int col=que.top().se,u=vec[col][vec[col].size()-1]; bool gg=0; for(int i=1;i<=N;i++) if(Ans[i]!=i&&Ans[i]){ int v=Ans[i]; if(A[v]!=col&&A[i]!=col){ gg=1; Ans[u]=i,Ans[i]=v,Ans[v]=u; break; } } if(!gg){ que.pop(); int xx=que.top().se; if(vec[xx].size()){ Ans[u]=vec[xx][vec[xx].size()-1];gg=1; } } if(!gg){printf("NO\n");continue;} A[0]=t; printf("YES\n"); for(int i=1;i<=N;i++) printf("%d ",A[Ans[i]]);printf("\n"); }else{ printf("YES\n"); A[0]=t; for(int i=1;i<=N;i++) printf("%d ",A[Ans[i]]);printf("\n"); } } }/* 6 4 5 3 3 3 1 1 1 */
[] CF1380G
为啥这种题值 $2600$ ?当前真宝箱为 $i$ 个,他们形成的连续段个数肯定为 $min(i,N-i)$ ,因为连续段个数肯定越多越好。则现在我们只需要对连续段填数即可。填数可以看做一个经典贪心,从大到小填即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 998244353 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; int N,A[MAXN],S[MAXN]; bool cmp(int x,int y){return x>y;} int Que(int l,int r){return l<=r?S[r]-S[l-1]:0;} int solve(int cnt,int l){ int tot=1,Ans=0; while(l+cnt-1<=N){Ans+=Que(l,l+cnt-1)%mod*tot%mod;Ans%=mod;l+=cnt,tot++;} Ans+=Que(l,N)%mod*tot%mod; Ans%=mod; return Ans; } int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} signed main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(); sort(A+1,A+N+1,cmp); for(int i=1;i<=N;i++) S[i]=S[i-1]+A[i]; for(int i=1;i<=N;i++){ if(i==N) printf("0\n"); else printf("%lld ",solve(min(i,N-i),i+1)*ksm(N,mod-2)%mod); } }/* 8 10 4 3 6 5 10 7 5 */
[] CF1380F
动态 $dp$ 模板题。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 998244353 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=5e5+11; struct Matrix{int A[3][3];void clear(){memset(A,0,sizeof(A));return;} }tr[MAXN<<2]; Matrix operator*(Matrix x1,Matrix x2){ Matrix x3;x3.clear(); for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) for(int k=1;k<=2;k++) x3.A[i][j]+=x1.A[i][k]*x2.A[k][j],x3.A[i][j]%=mod; return x3; } void print(Matrix s){printf("========\n");for(int i=1;i<=2;i++){for(int j=1;j<=2;j++) printf("%lld ",s.A[i][j]);printf("\n");}printf("========\n");return;} int F[MAXN],N,Q; char str[MAXN]; struct Segment{ void Modify(int k,int l,int r,int ps){ if(l==r){ int p=(19-(F[l]*10+F[l+1])),q=(F[l]+1); if(l==N||!F[l]||F[l]>1) p=0; tr[k].A[1][1]=q,tr[k].A[1][2]=p,tr[k].A[2][1]=1,tr[k].A[2][2]=0; //print(tr[k]); return; } int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps); else Modify(k<<1|1,mid+1,r,ps); tr[k]=tr[k<<1]*tr[k<<1|1]; } }S; signed main(){ //freopen("2.in","r",stdin); N=read(),Q=read(); scanf("%s",str+1); for(int i=1;i<=N;i++) F[i]=str[i]-'0'; for(int i=1;i<=N;i++) S.Modify(1,1,N,i); //print(tr[1]);return 0; while(Q--){ int ps=read(),w=read(); F[ps]=w; S.Modify(1,1,N,ps); if(ps!=1) S.Modify(1,1,N,ps-1); printf("%lld\n",(tr[1].A[1][1]+tr[1].A[1][2])%mod); } return 0; }
[] CF1375E
有一个不知道对不对的做法,考虑当前序列 $A$ 中若没有 $i$ 使得 $A_i> A_{i+1}$ 那么肯定 $A$ 是不严格单调递增的。
否则我们将 $i$ 与 $i+1$ 对应的原 $A$ 的下标翻转,最后倒叙输出即可。
正确性未知,但交了一发就过了???
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; int N,A[MAXN],id[MAXN]; vector<pii> vec,Ans; int main(){ N=read(); for(int i=1;i<=N;i++) A[i]=read(),id[i]=i; for(int i=1;i<=N;i++) for(int j=i+1;j<=N;j++) if(A[i]>A[j]) vec.pb(mp(i,j)); while(1){ int ps=-1; for(int i=1;i<N;i++) if(A[i]>A[i+1]){ps=i;break;} if(ps==-1) break; Ans.pb(mp(id[ps],id[ps+1])); swap(A[ps],A[ps+1]); swap(id[ps],id[ps+1]); } printf("%ld\n",Ans.size()); for(int i=Ans.size()-1;i>=0;i--) printf("%d %d\n",Ans[i].fi,Ans[i].se); return 0; //for(auto v:Ans) printf("%d %d\n",v.fi,v.se);return 0; }
[x] CF1375F
降智题目。 通过分析可以发现若当前石子数为 $a,b,c(a\leq b\leq c)$ ,且 $a,b,c$ 构成一个等差数列且 $c$ 在上次被选则先手赢。
那么我们考虑如何构造这种情况。考虑在倒退一步,再设三元组 $a,b,c$ ,其中 $c$ 上次被选,则我们只要让 $a/b$ 加上 $2c-a-b$ 的值肯定就可以构成一个符合上述规则的三元组。
我们考虑当先手,先让一堆变为最大,在加上 $2c-a-b$,最后只要在加上公差即可。 所以先手在 $3$ 步中必胜。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int A[5],x,y; signed main(){ for(int i=1;i<=3;i++) A[i]=read(); printf("First\n"); fflush(stdout); printf("10000000000\n"); fflush(stdout); x=read(); A[x]+=10000000000; int W; printf("%lld\n",W=(A[x]*3-A[1]-A[2]-A[3])); fflush(stdout); y=read(); A[y]+=W; printf("%lld\n",A[y]-A[x]); fflush(stdout); x=read(); return 0; }
[] CF1374E2
一道 $div3$ 的 $E$ 写一晚上。。。考虑将 $11,01,10,00$ 独立,那么我们枚举 $11$ 的同时我们就可以确定 $01,10$ 的最少个数。
若还剩 $p$ 个,那么肯定是在 $00,01,10$ 还未选择中选择 $p$ 个代价小的,可以看到这个过程可以用二分优化。
输出方案由于状态数过多需要先求出最小值后在去求解。时间复杂度 $O(n\log^2 n)$
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=4e5+11; int N,M,K,na,nb,nc,nd,Minn=LLONG_MAX,SA[MAXN],SB[MAXN],SC[MAXN],SD[MAXN]; int tmp1[MAXN],tmp2[MAXN],tmp3[MAXN],tmp4[MAXN]; pii A[MAXN],B[MAXN],C[MAXN],D[MAXN]; int sta1[MAXN],sta2[MAXN],sta3[MAXN],sta4[MAXN]; int QA(int x){return lower_bound(tmp1+1,tmp1+N+1,x+1)-tmp1-1;} int QB(int x){return lower_bound(tmp2+1,tmp2+N+1,x+1)-tmp2-1;} int QD(int x){return lower_bound(tmp4+1,tmp4+N+1,x+1)-tmp4-1;} signed main(){ //freopen("maker.in","r",stdin); N=read(),M=read(),K=read(); for(int i=1;i<=N;i++){int t=read(),a=read(),b=read();pii p=mp(t,i); if(a&&b) C[++nc]=p; if(a&&(!b)) A[++na]=p; if((!a)&&b) B[++nb]=p;if((!a)&&(!b)) D[++nd]=p;} for(int i=na+1;i<=N;i++) A[i]=mp(INT_MAX,0); for(int i=nb+1;i<=N;i++) B[i]=mp(INT_MAX,0); for(int i=nc+1;i<=N;i++) C[i]=mp(INT_MAX,0); for(int i=nd+1;i<=N;i++) D[i]=mp(INT_MAX,0); sort(A+1,A+N+1),sort(B+1,B+N+1),sort(C+1,C+N+1),sort(D+1,D+N+1); for(int i=1;i<=N;i++) SA[i]=SA[i-1]+A[i].fi,SB[i]=SB[i-1]+B[i].fi,SC[i]=SC[i-1]+C[i].fi,SD[i]=SD[i-1]+D[i].fi; for(int i=1;i<=N;i++) tmp1[i]=A[i].fi,tmp2[i]=B[i].fi,tmp3[i]=C[i].fi,tmp4[i]=D[i].fi; for(int i=0;i<=N;i++){ int res=(max(K-i,0ll))*2+i; if(res>M) continue; if(res<0) continue; int ps1=max(K-i,0ll),ps2=max(K-i,0ll),l=0,r=10000,Ans=-1; int Res=M-res; int u1=0,u2=0,u4=0; while(l<=r){ int mid=(l+r)>>1; int X=max(QA(mid)-ps1,0ll),Y=max(QB(mid)-ps2,0ll),Z=QD(mid); if(X+Y+Z>=Res) Ans=mid,u1=X,u2=Y,u4=Z,r=mid-1; else l=mid+1; } if(Ans==-1) continue; int W=SA[u1+ps1]+SB[u2+ps2]+SC[i]+SD[u4]; int Ha=(u1+ps1+u2+ps2+i+u4)-M; W-=Ha*Ans; Minn=min(Minn,W); } if(Minn>2000000000){printf("-1\n");return 0;} printf("%lld\n",Minn);//return 0; for(int i=0;i<=N;i++){ int res=(max(K-i,0ll))*2+i; if(res>M) continue; if(res<0) continue; int ps1=max(K-i,0ll),ps2=max(K-i,0ll),l=0,r=10000,Ans=-1; int Res=M-res; int u1=0,u2=0,u4=0; while(l<=r){ int mid=(l+r)>>1; int X=max(QA(mid)-ps1,0ll),Y=max(QB(mid)-ps2,0ll),Z=QD(mid); if(X+Y+Z>=Res) Ans=mid,u1=X,u2=Y,u4=Z,r=mid-1; else l=mid+1; } if(Ans==-1) continue; int W=SA[u1+ps1]+SB[u2+ps2]+SC[i]+SD[u4]; int Ha=(u1+ps1+u2+ps2+i+u4)-M; W-=Ha*Ans; if(Minn==W){ int tot1=0,tot2=0,tot3=0,tot4=0; for(int j=1;j<=u1+ps1;j++){if(A[j].fi!=Ans) printf("%lld ",A[j].se),tot1++; else sta1[++sta1[0]]=A[j].se;} for(int j=1;j<=u2+ps2;j++){if(B[j].fi!=Ans) printf("%lld ",B[j].se),tot2++; else sta2[++sta2[0]]=B[j].se;} for(int j=1;j<=u4;j++){if(D[j].fi!=Ans) printf("%lld ",D[j].se),tot4++; else sta4[++sta4[0]]=D[j].se;} for(int j=1;j<=i;j++){if(C[j].fi!=Ans) printf("%lld ",C[j].se),tot3++; else sta3[++sta3[0]]=C[j].se;} int E=M-tot1-tot2-tot3-tot4,ps1=1,ps2=1,ps3=1,ps4=1; for(int i=1;i<=E;i++){ if(tot1<tot2&&ps1<=sta1[0]){printf("%lld ",sta1[ps1]);ps1++;tot1++;continue;} if(tot1>tot2&&ps2<=sta2[0]){printf("%lld ",sta2[ps2]);ps2++;tot2++;continue;} if(ps3<=sta3[0]){printf("%lld ",sta3[ps3]);ps3++;continue;} if(ps1<=sta1[0]){printf("%lld ",sta1[ps1]);ps1++;continue;} if(ps2<=sta2[0]){printf("%lld ",sta2[ps2]);ps2++;continue;} if(ps4<=sta4[0]){printf("%lld ",sta4[ps4]);ps4++;continue;} }printf("\n"); return 0; } } return 0; }/* 6 3 1 6 0 0 11 1 0 9 0 1 21 1 1 10 1 0 8 0 1 */
[] CF1374F
假设 $A$ 是一个 $n$ 的排列,我们去观察一次操作的不变性。
考虑 $[1,2,3]-[3,1,2]-[2,3,1],[1,3,2]-[2,1,3]-[3,2,1]$,观察可以发现逆序对数每次操作只会 $+/- 2$ ,故如果原序列逆序对数是奇数则肯定无解。
而如果是偶数肯定可以构造出解,我们参考冒泡排序的排序手段,每次将最大数归位,则最后只会剩下前 $1,2,3$ 在最前面,由于逆序对数是偶数则必定可以通过最多两次操作复原。
故操作次数 $\leq n^2$ 。 而对于 $A$ 不是一个排列我们可以通过调整法将其调整成一个逆序对为奇数排列。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=511; int N,A[MAXN],T,P[MAXN],Num[MAXN]; vector<int> vec[MAXN],ANS; bool check(){for(int i=2;i<=N;i++) if(P[i-1]>P[i]) return 0;return 1;} int main(){ //freopen("5.in","r",stdin); T=read(); while(T--){ N=read(); P[0]=0; for(int i=1;i<=500;i++) vec[i].clear(); ANS.clear(); for(int i=1;i<=N;i++) A[i]=read(),vec[A[i]].pb(i); int Ans=0; for(int i=1;i<=N;i++) for(int j=i+1;j<=N;j++) if(A[i]>A[j]) Ans++; for(int i=1;i<=500;i++) for(auto v:vec[i]) P[v]=++P[0]; if(Ans&1){ memset(Num,0,sizeof(Num)); for(int i=1;i<=N;i++) Num[A[i]]++; int w=-1; for(int i=1;i<=N;i++) if(Num[i]>1){w=i;break;} if(w==-1){printf("-1\n");continue;} int ps1=0,ps2=0; for(int i=1;i<=N;i++){if(A[i]==w){if(!ps1) ps1=i;else{ps2=i;break;}}} swap(P[ps1],P[ps2]); } for(int i=N;i>=3;i--){ int ps;for(int j=1;j<=N;j++) if(P[j]==i) ps=j; for(int j=ps;j<i;j++){ if(j!=1){ ANS.pb(j-1); int x=P[j-1],y=P[j],z=P[j+1]; P[j]=x,P[j+1]=y,P[j-1]=z; }else{ ANS.pb(j); int x=P[j],y=P[j+1],z=P[j+2]; P[j+1]=x,P[j+2]=y,P[j]=z; } } } while(!check()){ ANS.pb(1); int x=P[1],y=P[2],z=P[3]; P[2]=x,P[3]=y,P[1]=z; } for(auto v:ANS){ int x=A[v],y=A[v+1],z=A[v+2]; A[v]=z,A[v+1]=x,A[v+2]=y; } printf("%ld\n",ANS.size()); for(auto v:ANS) printf("%d ",v);printf("\n"); }return 0; }/* 5 5 1 2 3 4 5 5 5 4 3 2 1 8 8 4 5 2 3 6 7 3 7 5 2 1 6 4 7 3 6 1 2 3 3 6 4 */
[] CF1373G
貌似可以不用 $Hall$ 定理? 显然,一个点 $(x,y)$ 到第 $k$ 行的的纵坐标 $\leq |x-k|+y$ 。
设 $\max\{|x-k|+y,n\}=p$ ,则问题转换成给你一些 $[s_i,p]$ 的线段使得给最多线段匹配一个互不相同的点。
那么我们可以处理 $S_i$ 表示在 $i-p$ 中的线段个数,则答案其实是 $\max\{s_i-(p-i+1),0\}+p$ 。 而这个东西也可以用 $Hall$ 定理来做,其实本质是类似的。。。
显然 $max$ 可以用线段树维护,而 $p$ 直接开个 $multiset$ 维护即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<map> #include<set> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; struct Segment{ int Maxn[MAXN<<2],tag[MAXN<<2]; void build(int k,int l,int r){ if(l==r){Maxn[k]=l-1;return;} int mid=(l+r)>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); Maxn[k]=max(Maxn[k<<1],Maxn[k<<1|1]); return; } void pushdown(int k){ if(!tag[k]) return; tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k]; Maxn[k<<1]+=tag[k],Maxn[k<<1|1]+=tag[k]; tag[k]=0; return; } void Modify(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){Maxn[k]+=w;tag[k]+=w;return;} int mid=(l+r)>>1; pushdown(k);if(x<=mid) Modify(k<<1,l,mid,x,y,w); if(mid<y) Modify(k<<1|1,mid+1,r,x,y,w); Maxn[k]=max(Maxn[k<<1],Maxn[k<<1|1]); return; } int Qmax(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Maxn[k]; pushdown(k); int mid=(l+r)>>1,res=0; if(x<=mid) res=max(res,Qmax(k<<1,l,mid,x,y)); if(mid<y) res=max(res,Qmax(k<<1|1,mid+1,r,x,y)); Maxn[k]=max(Maxn[k<<1],Maxn[k<<1|1]); return res; } }S; int N,K,Q,Lim; map<pii,bool> Ma; multiset<int> s; int main(){ //freopen("6.in","r",stdin); N=read(),K=read(),Q=read(); Lim=N+N+N; S.build(1,1,Lim); s.insert(N); while(Q--){ int X=read(),Y=read(),L=abs(K-X)+Y; int ff=Ma[mp(X,Y)],opt; if(ff) opt=-1,s.erase(s.find(L)); else opt=1,s.insert(L); Ma[mp(X,Y)]^=1; S.Modify(1,1,Lim,1,L,opt); int p=*(--s.end()); int E=S.Qmax(1,1,Lim,1,p); E=max(E,p); printf("%d\n",E-N); }return 0; }
[x] CF1370F
一道不是很难的交互题,我也不知道为啥没做出来?
考虑先询问全集则可以得到在 $(u,v)$ 路径上的点 $x$ 以及 $u,v$ 的长度 $h$ 。
然后很显然应该按 $x$ 为根建树。则现在只要在树上二分深度满足求最大的 $dep_u$ 使得 $lca_{u,v}=x且dep_u+dep_v=h$ 。
那么当前交换次数为 $1+\log_2 10^3+1=12$ 次,而对于 $hard$ 不难想到 $dep_u\leq \dfrac{h+1}{2}$ ,则可以在 $11$ 次内解决问题。
为啥这题代码又写了一晚上。。。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; vector<int> vec[MAXN],Que,g[MAXN]; int T,N,dep[MAXN],rt,L,Maxn,U,V,vis[MAXN],fa[MAXN]; pii p; char str[MAXN]; pii Ask(){ printf("? %ld ",Que.size()); for(auto v:Que) printf("%d ",v);printf("\n");fflush(stdout); int x=read(),d=read(); Que.clear();return mp(x,d); } void dfs(int u,int fath){ dep[u]=dep[fath]+1; g[dep[u]].pb(u); Maxn=max(Maxn,dep[u]); fa[u]=fath; for(auto v:vec[u]) if(v!=fath) dfs(v,u); } void dfs1(int u,int fath,int dep){ if(!dep&&!vis[u]) Que.pb(u); for(auto v:vec[u]) if(v!=fath) dfs1(v,u,dep-1); return; } bool ff=0; int main(){ T=read(); while(T--){ N=read(); for(int i=0;i<=N;i++) vec[i].clear(),g[i].clear(); Maxn=0; memset(vis,0,sizeof(vis)); for(int i=1;i<N;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} for(int i=1;i<=N;i++) Que.pb(i); p=Ask(); rt=p.fi,L=p.se; dep[0]=-1; dfs(rt,0); int l=(L+1)/2,r=min(Maxn,L),res=-1; while(l<=r){ int mid=(l+r)>>1;Que=g[mid]; p=Ask(); if(p.se==L) l=mid+1,res=mid,U=p.fi; else r=mid-1; }int pp=U; while(pp) vis[pp]=1,pp=fa[pp]; vis[rt]=0; dfs1(rt,0,L-res); p=Ask(); V=p.fi; printf("! %d %d\n",U,V); fflush(stdout); scanf("%s",str+1); }return 0; }
[x] CF1369E
我们假设每个人都吃了两个,允许食品总量为负数。
则若对于 $i$ 号食物在全吃完后的剩余量 $res_i\geq 0$,则肯定那些人不会无解,可以最后再吃这些物品。
由于这样必定是最优的,故拓扑排序即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; vector<pii> vec[MAXN]; int N,M,W[MAXN],Ans[MAXN],vis[MAXN]; queue<int> que; int main(){ N=read(),M=read();for(int i=1;i<=N;i++) W[i]=read(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[u].pb(mp(v,i)),vec[v].pb(mp(u,i)); W[u]--,W[v]--;} for(int i=1;i<=N;i++) if(W[i]>=0) que.push(i); while(!que.empty()){ int xx=que.front(); que.pop(); for(auto p:vec[xx]){ if(vis[p.se]) continue; vis[p.se]=1; Ans[++Ans[0]]=p.se; W[p.fi]++; if(W[p.fi]>=0) que.push(p.fi); } } if(Ans[0]!=M) printf("DEAD\n"); else{ printf("ALIVE\n"); for(int i=M;i>=1;i--) printf("%d ",Ans[i]);printf("\n"); }return 0; }
[x] CF1368E
神仙构造,想不到。
设 $A$ 集合为入度为 $0$ 的点已经所有入边都是 $C$ 的点。
$B$ 集合为有 $A$ 的入边且无 $B$ 的入边。
$C$ 集合为有 $B$ 的入边。
则 $A,B,C$ 三个集合肯定无交,且 $|C|\leq 2|B|\leq 4|A|$ ,则 $|C|\leq \dfrac{4}{7} n$ 。
而对于 $A,B$ 集合,所产生的边最长路径不会超过 $1$,选择 $C$ 集合即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; vector<int> vec[MAXN],Ans; int col[MAXN],N,M,T,Num[4]; int main(){ //freopen("3.in","r",stdin); T=read(); while(T--){ N=read(),M=read(); for(int i=1;i<=N;i++) vec[i].clear(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[v].pb(u);} for(int i=1;i<=N;i++){ if(!vec[i].size()){col[i]=0;continue;} memset(Num,0,sizeof(Num)); for(auto v:vec[i]) Num[col[v]]++; if(Num[2]&&!Num[1]&&!Num[0]){col[i]=0;continue;} if(Num[0]&&!Num[1]){col[i]=1;continue;} col[i]=2; } Ans.clear(); for(int i=1;i<=N;i++) if(col[i]==2) Ans.pb(i); printf("%ld\n",Ans.size()); for(auto v:Ans) printf("%d ",v);printf("\n"); }return 0; }
[x] CF1367F2
今天一道都不会。。。 将问题转化成求不动的最大值。
可以发现那些不动的点在离散化后只可能为三种情况
均为相同元素,$i,i,i,...,i$ 。
只有两种元素,分别为 $i,i+1$ , $i,i,i,..,i+1,i+1,i+1...$ 。
超过两种元素,$i,i,...,i,i+1,i+1,i+1,...,i+s,i+s,...,i+s$ ,其中 $i+1$ 到 $i+s-1$ 的元素必须都选满,因为他不能放在 $i$ 的左侧或 $i+s$ 的右侧。
则对上述 $dp$ ,设 $dp_{i,0/1/2}$ 表示当前选取 $i$ 号点,只选 $A_i$ 这种颜色,可以选 $A_i$ 以及当前 $A_i$ 已经选满的最大个数。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; int T,N,A[MAXN],f[MAXN][4],tmp[MAXN],M,L[MAXN],R[MAXN],pre[MAXN],Maxn; int main(){ //freopen("5.in","r",stdin); T=read(); while(T--){ memset(f,-127/3,sizeof(f)); memset(pre,0,sizeof(pre)); memset(L,0,sizeof(L)),memset(R,0,sizeof(R)); N=read(); for(int i=1;i<=N;i++) tmp[i]=A[i]=read(); sort(tmp+1,tmp+N+1); M=unique(tmp+1,tmp+N+1)-tmp-1; for(int i=1;i<=N;i++) A[i]=lower_bound(tmp+1,tmp+M+1,A[i])-tmp; for(int i=1;i<=N;i++){R[A[i]]=i; if(!L[A[i]]) L[A[i]]=i;} f[0][0]=f[0][1]=f[0][2]=0; Maxn=0; for(int i=1;i<=N;i++){ f[i][0]=f[pre[A[i]]][0]+1; f[i][1]=max(f[pre[A[i]]][1]+1,f[pre[A[i]-1]][0]+1); f[i][1]=max(f[i][1],f[pre[A[i]-1]][2]+1); if(i==R[A[i]]) f[i][2]=f[L[A[i]]][1]+f[i][0]-1; for(int j=0;j<=2;j++) Maxn=max(Maxn,f[i][j]); pre[A[i]]=i; //cerr<<"i:"<<i<<" f(0):"<<f[i][0]<<" f(1):"<<f[i][1]<<" f(2):"<<f[i][2]<<endl; } printf("%d\n",N-Maxn); }return 0; }/* 5 4 7 2 2 9 */
[] CF1363F
可以发现一次操作 $[A_l,A_{l+1},...,A_r]$ 变为 $[A_r,A_l,A_{l+1},...,A_{r-1}]$ 可以看做将 $A_r$ 换到任意在它前面字符的前面。
则无解情况就是二者的某一字符出现次数不同,否则肯定有解。
可以看出 $A_r$ 最多会往前 $1$ 次,则我们让不动点最多,即选择最多点的点集使得其字符相同。
问题相当于求两个串的 $lcs$ ,而对于 $S_i=T_j$ 时我们需要取判断是否将 $i$ 点固定是否有解,即 $S$ 后缀的字符数均大于等于 $T$ 的字符数。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2011; int f[MAXN][MAXN],Num[2][MAXN][27],N,cas; char S[MAXN],T[MAXN]; bool Get(int x,int y){ for(int i=0;i<26;i++) if(Num[0][x][i]<Num[1][y][i]) return 0; return 1; } int main(){ //freopen("1.in","r",stdin); cas=read(); while(cas--){ N=read(); scanf("%s%s",S+1,T+1); for(int i=0;i<=N+1;i++) for(int j=0;j<=N+1;j++) f[i][j]=INT_MIN; for(int i=0;i<=N+1;i++) for(int j=0;j<26;j++) Num[0][i][j]=Num[1][i][j]=0; for(int i=N;i>=1;i--){ for(int j=0;j<26;j++) Num[0][i][j]=Num[0][i+1][j]; Num[0][i][S[i]-'a']++; for(int j=0;j<26;j++) Num[1][i][j]=Num[1][i+1][j]; Num[1][i][T[i]-'a']++; } bool ff=1; for(int i=0;i<26;i++) ff&=(Num[0][1][i]==Num[1][1][i]); if(!ff){printf("-1\n");continue;} f[0][0]=0; for(int i=0;i<=N;i++) f[0][i]=f[i][0]=0; for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ f[i][j]=max(f[i-1][j],f[i][j-1]); if(Get(i,j)&&S[i]==T[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1); } } printf("%d\n",N-f[N][N]); } return 0; }
[x] CF1361C
不会欧拉图。 由于答案很小具有单调性倒叙枚举。
则问题转换成求一个环,环上每个点代表一个点对,点对之间连边表示其相连部分权值相同。
这是一个比较经典的欧拉路径问题,可以将每个点对映射到两端的权值上,求是否存在欧拉回路即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1048577; int N,a[MAXN],b[MAXN],tot,A[MAXN],B[MAXN],now[MAXN]; vector<int> Ans,co[MAXN],ban[MAXN]; vector<pair<int,pii>> vec[MAXN]; void dfs(int u){ for(int &i=now[u];i<vec[u].size();i++){ if(ban[u][i]) continue; int v=vec[u][i].fi,X=vec[u][i].se.fi,Y=vec[u][i].se.se; ban[u][i]=ban[v][co[u][i]]=1; dfs(v); Ans.pb(Y),Ans.pb(X); }return; } void solve(int x){ Ans.clear();tot=N; int M=(1<<(x))-1; for(int i=0;i<=M;i++) vec[i].clear(),co[i].clear(),ban[i].clear(); for(int i=1;i<=N;i++) A[i]=a[i]&M,B[i]=b[i]&M; //for(int i=1;i<=N;i++) printf("%d %d\n",A[i],B[i]); for(int i=1;i<=N;i++){ vec[A[i]].pb(mp(B[i],mp(2*i-1,2*i))),vec[B[i]].pb(mp(A[i],mp(2*i,2*i-1))); co[A[i]].pb(vec[B[i]].size()-1); co[B[i]].pb(vec[A[i]].size()-1-(A[i]==B[i])); ban[A[i]].pb(0),ban[B[i]].pb(0); } memset(now,0,sizeof(now)); for(int i=0;i<=M;i++) if(vec[i].size()&1) return; dfs(A[1]); if(Ans.size()!=N*2) return; printf("%d\n",x); for(int i=2*N-1;i>=0;i--) printf("%d ",Ans[i]);printf("\n");exit(0); } int main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<=N;i++) a[i]=read(),b[i]=read(); for(int i=20;i>=0;i--) solve(i); }
[] CF1358E
当 $x>0$ 时,显然若长度为 $n$ 不满足规定则其余均不满足规定。
当 $x\leq 0$ 时,我们考虑长度肯定会超过 $\dfrac{n+1}{2}$ ,则若在左区间选择 $i$ ,则右区间的选择范围是单调的!
所以二分查找右端点最大值,每次首次超过 $n$ 的位置。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=5e5+11; int N,A[MAXN],X,S[MAXN],res; void solve(){ int L=0,R=N+1; for(int i=1;i<=(N+1)/2;i++){ int l=res+1,r=N; if(l>r) return; int Res=-1; while(l<=r){ int mid=(l+r)>>1; if(S[mid]>S[i-1]) Res=mid,l=mid+1; else r=mid-1; } if(res==-1) return; L=max(L,res-i+1),R=min(R,Res-i+1); //if(i+R>N){printf("%lld\n",R);exit(0);} int Len=N-i; if(L<=N-i+1&&N-i+1<=R){printf("%lld\n",N-i+1);exit(0);} } if(L<=R){printf("%lld\n",L);exit(0);} return; } signed main(){ //freopen("2.in","r",stdin); N=read(); for(int i=1;i<=(N+1)/2;i++) A[i]=read(); res=N-(N+1)/2; X=read(); for(int i=(N+1)/2+1;i<=N;i++) A[i]=X; for(int i=1;i<=N;i++) S[i]=S[i-1]+A[i]; int k=(X<=0)?res+1:N; bool ff=1; for(int i=1;i+k-1<=N;i++) ff&=(S[i+k-1]>S[i-1]); if(ff) printf("%lld\n",k); else { if(X<=0) solve(); printf("-1\n"); return 0; } }/* 4 1000000000 999999999 -1000000000 */
[] CF1469E
首先无解的情况是将 $n$ 的所有长度为 $k$ 的字符串取出去重后个数为 $2^k$ 。
则我们发现若 $k>20$ ,则我们只要让除后 $20$ 个外其余都为 $0$ 肯定有解。
则现在只要考虑 $k\leq 20$ 的情况,我们只需要维护一段区间的个数是否为 $2^p$ 贪心即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<map> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=4e6+11; int A[MAXN],N,K,cas,S[MAXN],Ans[MAXN],now,fac[21]; char str[MAXN]; map<int,bool> Ma; int main(){ //freopen("2.in","r",stdin); fac[0]=1; for(int i=1;i<=20;i++) fac[i]=fac[i-1]*2; cas=read(); while(cas--){ N=read(),K=read(); scanf("%s",str+1); A[0]=0; Ma.clear(); for(int i=1;i<=N;i++) S[i]=S[i-1]+(str[i]=='0'); if(K>=21){ for(int i=1;i<=N-K+1;i++){ int L=i+K-20,R=i+K-1,res=0; if(S[i-1]!=S[L-1]) continue; for(int j=L;j<=R;j++) res=res*2+str[j]-'0'; if(Ma[res]) continue; Ma[res]=1; A[++A[0]]=res; } now=20; }else{ for(int i=1;i<=N-K+1;i++){ int L=i,R=i+K-1,res=0; for(int j=L;j<=R;j++) res=res*2+str[j]-'0'; if(Ma[res]) continue; Ma[res]=1; A[++A[0]]=res; } now=K; } sort(A+1,A+A[0]+1); int L=1,R=A[0]; bool ff=1; for(int i=now;i>=1;i--){ int l=L,r=R,res=R+1; while(l<=r){ int mid=(l+r)>>1; if(A[mid]&(1<<(i-1))) res=mid,r=mid-1; else l=mid+1; } int cnt1=R-res+1,cnt0=R-L+1-cnt1; if(cnt1!=fac[i-1]){Ans[i]=0,L=res;continue;} if(cnt0!=fac[i-1]){Ans[i]=1,R=res-1;continue;} ff=0; break; } if(!ff){printf("NO\n");continue;} printf("YES\n"); for(int i=K;i>=1;i--) printf("%d",Ans[i]);printf("\n"); } }
[] CF1469F
猜一发链按照深度从大到小中点折半插进去是最优的。再猜一发答案不会太大? 证明,无。
线段树维护即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=4e5+11; int Minn=INT_MAX; struct Segment{ int tag[MAXN<<2],sum[MAXN<<2]; void pushdown(int k,int l,int r){ if(!tag[k]) return; int mid=(l+r)>>1; tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k]; sum[k<<1]+=tag[k]*(mid-l+1),sum[k<<1|1]+=tag[k]*(r-mid); tag[k]=0;return; } void Modify(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){sum[k]+=(r-l+1)*w,tag[k]+=w;return;} int mid=(l+r)>>1; pushdown(k,l,r); if(x<=mid) Modify(k<<1,l,mid,x,y,w); if(mid<y) Modify(k<<1|1,mid+1,r,x,y,w); sum[k]=sum[k<<1]+sum[k<<1|1]; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return sum[k]; pushdown(k,l,r); int mid=(l+r)>>1,res=0; if(x<=mid) res+=Query(k<<1,l,mid,x,y); if(mid<y) res+=Query(k<<1|1,mid+1,r,x,y); sum[k]=sum[k<<1]+sum[k<<1|1]; return res; } void find(int k,int l,int r,int res){ if(l==r){Minn=min(Minn,l-1);return;} int mid=(l+r)>>1; pushdown(k,l,r); if(sum[k<<1]>=res) find(k<<1,l,mid,res); else find(k<<1|1,mid+1,r,res-sum[k<<1]); sum[k]=sum[k<<1]+sum[k<<1|1]; return; } }S; int N,K,A[MAXN],M; bool cmp(int x,int y){return x>y;} signed main(){ //freopen("maker.in","r",stdin); N=read(),K=read(); for(int i=1;i<=N;i++) A[i]=read(); M=400000; sort(A+1,A+N+1,cmp); S.Modify(1,1,M,1,1,1); int dep=1; for(int i=1;i<=N;i++){ while(!S.Query(1,1,M,dep,dep)) dep++; S.Modify(1,1,M,dep,dep,-1); //cerr<<"i:"<<i<<" dep:"<<dep<<endl; int w=A[i]; if(w&1) S.Modify(1,1,M,dep+2,dep+1+(w-1)/2,2); else S.Modify(1,1,M,dep+2,dep+w/2,2),S.Modify(1,1,M,dep+w/2+1,dep+w/2+1,1); //cerr<<"haha"<<" "<<S.Query(1,1,M,1,M)<<" dep:"<<dep<<endl; if(S.Query(1,1,M,1,M)>=K) S.find(1,1,M,K); } if(Minn!=INT_MAX) printf("%lld\n",Minn); else printf("-1\n"); return 0; }