Educational Codeforces Round 112 (Rated for Div. 2)
A
因为所有方案的平均价格是一样的所以我们就可以考虑每个方案怎么能被最少地凑出来。
如果是奇数那么肯定要加一变成偶数。然后如果\(>6\)一定能被凑出来。\(\leq6\)只能输出\(15\)
code:
using namespace std;
int T;ll n;
int main(){
freopen("1.in","r",stdin);
scanf("%d",&T);while(T--){
scanf("%lld",&n);n+=n&1;if(n<=6) printf("15\n");
else printf("%lld\n",n*5/2);
}
}
B
分放在左边,右边,上面,下面四种情况讨论即可。题目中那个小数是骗人的。
code:
using namespace std;
int T,W,H,nx,ny,mx,my,w,h,Ans;
int main(){
freopen("1.in","r",stdin);
scanf("%d",&T);while(T--){
scanf("%d%d",&W,&H);scanf("%d%d%d%d",&nx,&ny,&mx,&my);scanf("%d%d",&w,&h);
if(W<w+mx-nx&&H<h+my-ny){printf("-1\n");continue;}
Ans=1e9;if(W>=w+mx-nx){
Ans=min(Ans,min(max(w-nx,0),max(w-(W-mx),0)));
}
if(H>=h+my-ny){
Ans=min(Ans,min(max(h-ny,0),max(h-(H-my),0)));
}printf("%.9lf\n",1.0*Ans);
}
}
C
因为只有两行所以我们考虑枚举A往下走的点。
因为B也只能往下和往右所以只能在A剩下的上下两端中选最大的一段,然后A可以在所有\(n\)种情况中取min
时间复杂度\(O(n)\)
code:
using namespace std;
int T,n,A[N+5],Q1[N+5],Q2[N+5],Ans;
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d",&T);while(T--){
scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&A[i]);Q1[n+1]=0;for(i=n;i;i--)Q1[i]=Q1[i+1]+A[i];for(i=1;i<=n;i++) scanf("%d",&A[i]),Q2[i]=Q2[i-1]+A[i];
Ans=2e9;for(i=1;i<=n;i++) Ans=min(Ans,max(Q1[i+1],Q2[i-1]));printf("%d\n",Ans);
}
}
D
这个只有三个字符的限制十分诡异所以我们从这里入手。
手玩一下发现情况只有abc和cba或他们的循环重构。
对于这六种情况前缀和一下然后取min即可。
code:
using namespace std;
int T,n,m,Ans,A[N+5],Q[10][N+5],x,y; char S[N+5],S1[N+5],S2[N+5],S3[N+5];
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d%d",&n,&m);scanf("%s",S+1);
for(i=1;i<=n/3+2;i++) S2[i*3-2]='a',S2[i*3-1]='b',S2[i*3]='c';
for(i=1;i<=n;i++) Q[4][i]=Q[4][i-1]+(S2[i]==S[i]),Q[5][i]=Q[5][i-1]+(S2[i+1]==S[i]),Q[6][i]=Q[6][i-1]+(S2[i+2]==S[i]);
for(i=1;i<=n/3+2;i++) S3[i*3-2]='c',S3[i*3-1]='b',S3[i*3]='a';
for(i=1;i<=n;i++) Q[7][i]=Q[7][i-1]+(S3[i]==S[i]),Q[8][i]=Q[8][i-1]+(S3[i+1]==S[i]),Q[9][i]=Q[9][i-1]+(S3[i+2]==S[i]);
while(m--){
scanf("%d%d",&x,&y);Ans=0;for(i=4;i<10;i++) Ans=max(Ans,Q[i][y]-Q[i][x-1]);printf("%d\n",y-x+1-Ans);
}
}
E
这种最大减最小的题目肯定一眼二分,但是这道题不用二分。
考虑枚举权值的左端点然后看最近的右端点能联通\(1\)和\(n\)
右端点显然单调所以可以用双指针维护。
判断\(1\)和\(n\)是否联通可以用标记永久化的线段树维护。一个小细节就是两个一定要有重复,解决办法是把一个点拆成两个然后如果没有重复中间会空一个点。
时间复杂度\(O(nlogn)\)
code:
using namespace std;
int n,m,r,Ans=1e9,Fl[M+5<<3],Sum[M+5<<3],Maxn,x,y,z;
struct ques{int x,y;}now;vector<ques> G[M+5];
I void Up(int now){Sum[now]=Sum[now<<1]+Sum[now<<1|1];}
I void getins(int x,int y,int l=1,int r=m,int now=1){
if(x<=l&&r<=y){Fl[now]++;Sum[now]=r-l+1;return;}int m=l+r>>1;x<=m&&(getins(x,y,l,m,now<<1),0);y>m&&(getins(x,y,m+1,r,now<<1|1),0);Sum[now]=(Fl[now]?r-l+1:Sum[now<<1]+Sum[now<<1|1]);
}
I void getdel(int x,int y,int l=1,int r=m,int now=1){
if(x<=l&&r<=y){Fl[now]--;Sum[now]=(Fl[now]?r-l+1:Sum[now<<1]+Sum[now<<1|1]);return;}int m=l+r>>1;x<=m&&(getdel(x,y,l,m,now<<1),0);y>m&&(getdel(x,y,m+1,r,now<<1|1),0);Sum[now]=(Fl[now]?r-l+1:Sum[now<<1]+Sum[now<<1|1]);
}
I void ins(int x){for(int i=0;i<G[x].size();i++) now=G[x][i],getins(now.x*2-1,now.y*2-1);}
I void del(int x){for(int i=0;i<G[x].size();i++) now=G[x][i],getdel(now.x*2-1,now.y*2-1);}
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d%d",&n,&m);m<<=1;for(i=1;i<=n;i++) scanf("%d%d%d",&x,&y,&z),G[z].push_back((ques){x,y}),Maxn=max(Maxn,z);
for(i=1;i<=Maxn;i++){
del(i-1);while(r<=Maxn&&Sum[1]<m-1) ins(r++);if(Sum[1]<m-1) break;Ans=min(Ans,r-i-1);
}printf("%d\n",Ans);
}
F
手玩一下就可以发现这个图的性质:如果加入的形成的环满足条件且加入后仍然是仙人掌那么就可以加入。
这个东西显然可以LCT维护但是那个东西就是我赛时没调出来的罪魁祸首。
考虑如果一条边加入之前两个端点不连通那么这条边一定是可以加入的因为加入后不形成环。
那么这个树的形态是确定的。
所以可以直接树剖,维护到根节点的异或和这样就可以异或出两个点路径的权值。
如果一条边可以加入,那么将这路径上的边全部打上标记,这样只要包含有标记的边就不能加入。
时间复杂度\(O(n+q)log^2n\)但是跑得出奇地快。
code:
using namespace std;
int n,m,k,Fl[N+5],x[N+5],y[N+5],z[N+5],Ans[N+5],Q[N+5],Fa[N+5],fa[N+5],d[N+5],top[N+5],id[N+5],siz[N+5],idea,son[N+5],F[N+5<<2],G[N+5<<2],un,wn;
struct yyy{int to,w,z;};
struct ljb{int head,h[N+5];yyy f[N+5<<2];I void add(int x,int y,int z){f[++head]=(yyy){y,z,h[x]};h[x]=head;}}s;
I int Getfa(int x){return x==Fa[x]?x:Fa[x]=Getfa(Fa[x]);}
I void dfs1(int x,int last){
d[x]=d[last]+1;yyy tmp;siz[x]=1;fa[x]=last;for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^last&&(Q[tmp.to]=Q[x]^tmp.w,dfs1(tmp.to,x),siz[x]+=siz[tmp.to],siz[son[x]]<siz[tmp.to]&&(son[x]=tmp.to));
}
I void dfs2(int x,int last){
top[x]=last;id[x]=++idea;yyy tmp;if(!son[x]) return;dfs2(son[x],last);for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^fa[x]&&tmp.to^son[x]&&(dfs2(tmp.to,tmp.to),0);
}
I void insert(int x,int y,int l=1,int r=n+1,int now=1){
F[now]=1;if(x<=l&&r<=y) return (void)(G[now]=1);int m=l+r>>1;x<=m&&(insert(x,y,l,m,now<<1),0);y>m&&(insert(x,y,m+1,r,now<<1|1),0);
}
I int find(int x,int y,int l=1,int r=n+1,int now=1){//printf("%d %d\n",x,y);
if(G[now]) return 1;if(x<=l&&r<=y) return F[now];int m=l+r>>1;return (x<=m&&find(x,y,l,m,now<<1))||(y>m&&find(x,y,m+1,r,now<<1|1));
}I void swap(int &x,int &y){x^=y^=x^=y;}
I void Get(int x,int y){while(top[x]^top[y])d[top[x]]<d[top[y]]&&(swap(x,y),0),insert(id[top[x]],id[x]),x=fa[top[x]];d[x]>d[y]&&(swap(x,y),0);x^y&&(insert(id[x]+1,id[y]),0);}
I int Query(int x,int y){while(top[x]^top[y]) {d[top[x]]<d[top[y]]&&(swap(x,y),0);if(find(id[top[x]],id[x]))return 1;x=fa[top[x]];}d[x]>d[y]&&(swap(x,y),0);return x^y&&find(id[x]+1,id[y]);}
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) Fa[i]=i;for(i=1;i<=m;i++) scanf("%d%d%d",&x[i],&y[i],&z[i]);for(i=1;i<=m;i++) un=Getfa(x[i]),wn=Getfa(y[i]),un^wn&&(Fa[un]=wn,s.add(x[i],y[i],z[i]),s.add(y[i],x[i],z[i]),Ans[i]=1);
for(i=1;i<=n;i++) Fl[Getfa(i)]=1;for(i=1;i<=n;i++)Fl[i]&&(s.add(n+1,i,0),0);dfs1(n+1,0);dfs2(n+1,n+1);for(i=1;i<=m;i++)!Ans[i]&&Q[x[i]]^Q[y[i]]^z[i]&&!Query(x[i],y[i])&&(Get(x[i],y[i]),Ans[i]=1);for(i=1;i<=m;i++)puts(Ans[i]?"YES":"NO");
}