Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)
A. Math Problem
$solution:$
直接找右端点最小值与左端点的最大值做差比较即可,时间复杂度 $O(n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long using namespace std; signed main(){ int T; cin>>T; while(T--){ int N,l,r; cin>>N; cin>>l>>r; for(int i=2;i<=N;i++){ int a,b; cin>>a>>b; l=max(l,a),r=min(r,b); } printf("%lld\n",max(0ll,l-r)); }return 0; }
B. Box
$solution:$
随便写即可,时间复杂度 $O(n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> 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=100011; int N,vis[MAXN],A[MAXN],B[MAXN],T,G[MAXN]; bool flag; int main(){ T=read(); while(T--){ flag=1; N=read(); for(int i=1;i<=N;i++) vis[i]=0; for(int i=1;i<=N;i++) A[i]=read(); B[1]=A[1];vis[A[1]]=1; int ps=1; for(int i=2;i<=N;i++){ if(A[i-1]==A[i]){ while(ps<=N&&vis[ps]) ps++; if(ps==N+1){flag=0;break;} if(ps>A[i]){flag=0;break;} B[i]=ps;vis[ps]=1; continue; } if(vis[A[i]]){flag=0;break;} B[i]=A[i];vis[A[i]]=1; } if(!flag){ printf("-1\n");continue; } for(int i=1;i<=N;i++) G[i]=max(G[i-1],A[i]); for(int i=1;i<=N;i++) flag&=(G[i]==A[i]); if(flag){ for(int i=1;i<=N;i++) printf("%d ",B[i]); printf("\n"); continue; } printf("-1\n"); }return 0; }/* 2 5 5 5 5 5 5 */
C. Messy
$solution:$
若为 $k$ 个前缀可以构造成 $k-1$ 个 $()$ 与 $(((())))$ ,直接暴力即可,时间复杂度 $O(n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> 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=2001; vector<pair<int,int> > vec; int T,N,K; char str[MAXN]; int A[MAXN],B[MAXN]; int main(){ // freopen("8.in","r",stdin); T=read(); while(T--){ vec.clear(); N=read(),K=read(); for(int i=1;i<=N;i++) A[i]=B[i]=0; for(int i=1;i<=K-1;i++){ A[2*i-1]=1,A[2*i]=0; } int E=2*(K-1),res=(N-E)/2; for(int i=E+1;i<=E+res;i++) A[i]=1; scanf("%s",str+1); for(int i=1;i<=N;i++) B[i]=(str[i]=='('); for(int i=1;i<=N;i++){ if(A[i]==B[i]) continue; int ps; for(int j=i+1;j<=N;j++){ if(B[j]==A[i]){ps=j;break;} } reverse(B+i,B+ps+1); vec.push_back(make_pair(i,ps)); } printf("%d\n",vec.size()); for(int i=0;i<vec.size();i++) printf("%d %d\n",vec[i].first,vec[i].second); }return 0; }/* 4 8 2 ()(())() 10 3 ))()()()(( 2 1 () 2 1 )( */
D. Optimal Subsequences
$solution:$
容易发现最后子序列的选择肯定是先去权值大的,若相同取坐标靠前的,这样既可以保证和最大也可以保证字典序最小。
询问可以离线下来线段树二分查找下标位置,时间复杂度 $O(n\log n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> 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=200001; struct Node{ int val,id; }F[MAXN]; int N,A[MAXN]; bool cmp(Node x1,Node x2){ if(x1.val==x2.val) return x1.id<x2.id; return x1.val>x2.val; } struct Segment_Tree{ int Ans[MAXN<<2]; void Modify(int k,int l,int r,int ps){ Ans[k]++;if(l==r) return; int mid=l+r>>1; if(ps<=mid) Modify(k<<1,l,mid,ps); if(mid<ps) Modify(k<<1|1,mid+1,r,ps); return; } int Query(int k,int l,int r,int W){ if(l==r) return l; int mid=l+r>>1; if(Ans[k<<1]>=W) return Query(k<<1,l,mid,W); return Query(k<<1|1,mid+1,r,W-Ans[k<<1]); } }Segment; int Ans[MAXN],q; vector<pair<int,int> > vec[MAXN]; int main(){ N=read(); for(int i=1;i<=N;i++) A[i]=F[i].val=read(),F[i].id=i; sort(F+1,F+N+1,cmp); q=read(); for(int i=1;i<=q;i++){ int u=read(),v=read(); vec[u].push_back(make_pair(i,v)); } for(int i=1;i<=N;i++){ Segment.Modify(1,1,N,F[i].id); for(int j=0;j<vec[i].size();j++){ int p=vec[i][j].second,id=vec[i][j].first; Ans[id]=A[Segment.Query(1,1,N,p)]; } } for(int i=1;i<=q;i++) printf("%d\n",Ans[i]);return 0; }/* 3 10 20 10 1 2 1 */
E.Arson In Berland Forest
$solution:$
考虑二分答案后若可以变为一开始着火点就变,最后判断是否可行。对矩形做前缀和后对于二分答案差分得到是否可行。时间复杂度 $O(nm\log n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #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=1000011; vector<int> S[MAXN],A[MAXN],F[MAXN]; int N,M; char str[MAXN]; bool ok(int x,int y){return x>=1&&x<=N&&y>=1&&y<=M;} int calc(int x1,int y1,int x2,int y2){return S[x2][y2]-S[x1-1][y2]-S[x2][y1-1]+S[x1-1][y1-1];} void Modify(int x1,int y1,int x2,int y2,int w){if(x2<N&&y2<M) F[x2+1][y2+1]+=w;F[x1][y1]+=w;if(y2<M) F[x1][y2+1]-=w;if(x2<N) F[x2+1][y1]-=w;return;} bool checker(int Lim){ for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) F[i][j]=0; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++){ int x1=i-Lim,y1=j-Lim,x2=i+Lim,y2=j+Lim; if(ok(x1,y1)&&ok(x2,y2)){ if(calc(x1,y1,x2,y2)==(2*Lim+1)*(2*Lim+1)){ Modify(x1,y1,x2,y2,1); } } } for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) F[i][j]+=F[i-1][j]+F[i][j-1]-F[i-1][j-1]; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if((bool)F[i][j]!=A[i][j]) return 0; return 1; } signed main(){ // freopen("10.in","r",stdin); N=read(),M=read(); for(int i=0;i<=N;i++) S[i].resize(M+1),A[i].resize(M+1),F[i].resize(M+1); for(int i=1;i<=N;i++){ scanf("%s",str+1); for(int j=1;j<=M;j++) A[i][j]=(str[j]=='X'),S[i][j]=A[i][j]; } for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) S[i][j]+=S[i-1][j]+S[i][j-1]-S[i-1][j-1]; int l=0,r=N,res=0; while(l<=r){ int mid=l+r>>1; if(checker(mid)) res=mid,l=mid+1; else r=mid-1; }printf("%lld\n",res);int Lim=res; for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++){ bool f=1; int x1=i-Lim,y1=j-Lim,x2=i+Lim,y2=j+Lim; if(ok(x1,y1)&&ok(x2,y2)){ if(calc(x1,y1,x2,y2)==(2*Lim+1)*(2*Lim+1)) f=0,printf("X"); } if(f) printf("."); }printf("\n"); } return 0; }/* 3 6 XXXXXX XXXXXX XXXXXX */
F. Wrong Answer on test 233
考虑 $O(n^2)$ $dp$ 的优化,发现当 $A_i\neq A_{i+1}$ 是会对答案产生 $1$ 或 $k-2$ 的贡献。
而我们考虑若设选择第 $i$ 个位置选择 $A_i$ 为 $-1$ ,选择 $A_{i+1}$ 为 $+1$ ,则会发现最后值大于 $0$ 与小于 $0$ 的个数相同,因为可以对于每个 $+1$ 变为 $-1$ ,$-1$ 变为 $+1$ ,两个方案互相对应,则考虑容斥得到值等于 $0$ 的方案数 $Ans$ ,答案即为 $\dfrac{k^n-Ans}{2}$ 。
而 $Ans=\sum_{i} \dbinom{cnt}{i}\cdot\dbinom{cnt-i}{i}\cdot (k-2)^{cnt-2*i}\cdot k^{n-cnt}$ 。
时间复杂度 $O(n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #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=200001; int fac[MAXN],ifac[MAXN],N,K,inv[MAXN],cnt,Ans,A[MAXN<<1],pw1[MAXN],pw2[MAXN]; int C(int a,int b){return fac[a]*ifac[b]%mod*ifac[a-b]%mod;} int Mod(int x){return ((x%mod)+mod)%mod;} 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("1.txt","r",stdin); N=read(),K=read(); inv[1]=1;for(int i=2;i<MAXN;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; fac[0]=1;for(int i=1;i<MAXN;i++) fac[i]=fac[i-1]*i%mod; ifac[0]=1;for(int i=1;i<MAXN;i++) ifac[i]=ifac[i-1]*inv[i]%mod; pw1[0]=pw2[0]=1;for(int i=1;i<MAXN;i++) pw1[i]=pw1[i-1]*K%mod,pw2[i]=pw2[i-1]*(K-2)%mod;; Ans=ksm(K,N);for(int i=1;i<=N;i++) A[i]=A[i+N]=read(); for(int i=1;i<=N;i++) cnt+=(A[i]!=A[i+1]);int res=0; for(int i=0;;i++){ if(cnt<2*i) break; res+=C(cnt,i)*C(cnt-i,i)%mod*pw2[cnt-2*i]%mod*pw1[N-cnt]%mod; res%=mod; } Ans=Mod(Ans-res);printf("%lld\n",Ans*inv[2]%mod);return 0; }/* 6 2 1 1 2 2 1 1 */