zoukankan      html  css  js  c++  java
  • CodeForces 1385

    今天下午看到有7个题,打了个vp,一开始准备用chenxiaoyan打的,然后调错时间了,无奈只能用小号WinnieThePooh打/xk

    最终还剩几分钟的时候AK了/cy

    吐槽一下,这场咋全多测啊?

    CF比赛页面传送门

    A - Three Pairwise Maximums

    洛谷还没爬,下同,管理员快爪巴 & CF题目页面传送门

    题意紫帆。

    考虑将(a,b,c)从小到大排序,则(x=b,y=z=c)。将题目给出的(x,y,z)排序之后,若(y eq z)则无解,否则(a=1,b=x,c=y)是一组解。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    void mian(){
    	int x,y,z;
    	scanf("%d%d%d",&x,&y,&z);
    	if(x>y)swap(x,y);if(y>z)swap(y,z);
    	if(x>y)swap(x,y);if(y>z)swap(y,z);
    	if(y!=z)return puts("NO"),void();
    	puts("YES");
    	cout<<1<<" "<<x<<" "<<y<<"
    ";
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    B - Restore the Permutation by Merger

    CF题目页面传送门

    题意紫帆。

    (1sim n)所有数以第一次出现的位置取下来,相对位置不变,组成一个排列,就是答案。自证不难。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=50;
    int n;
    bool hav[N+1];
    void mian(){
    	cin>>n;
    	memset(hav,0,sizeof(hav));
    	for(int i=1;i<=2*n;i++){
    		int x;
    		scanf("%d",&x);
    		if(!hav[x])hav[x]=true,printf("%d ",x);
    	}
    	puts("");
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    C - Make It Good

    CF题目页面传送门

    题意紫帆。

    不难发现一个数列是好的当且仅当它非严格单峰。自证不难。又发现,删掉前缀相当于留下后缀(这个出题人迷惑的太失败了),右端点是不变的。于是贪心,从右往左找到最左边的一个可以当峰值的位置,然后再从峰值往左边找到最左边的左端点。最终答案就是左端点减一。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200000;
    int n;
    int a[N+1];
    void mian(){
    	cin>>n;
    	for(int i=1;i<=n;i++)scanf("%d",a+i);
    	int las;
    	for(int i=n;i;i--){//找最左峰值 
    		if(i<n&&a[i]<a[i+1])break;
    		las=i;
    	}
    	int las0;
    	for(int i=las;i;i--){//找最左左端点 
    		if(i<las&&a[i]>a[i+1])break;
    		las0=i;
    	}
    	cout<<las0-1<<"
    ";
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    D - a-Good String

    CF题目页面传送门

    题意紫帆。

    做法很显然,考虑DP,设(dp_{l,r,x})表示子串(s_{lsim r})成为(x)-good string所需要的最小操作数。边界:(dp_{l,l,x}=[s_l eq x]),目标:(dp_{1,n, exttt a}),转移:(dp_{l,r,x}=minleft(sumlimits_{i=l}^{frac{l+r-1}2}[s_i eq x]+dp_{frac{l+r-1}2+1,r,x+1},sumlimits_{i=frac{l+r-1}2+1}^{r}[s_i eq x]+dp_{l,frac{l+r-1}2,x+1} ight))

    然而这样看起来空间是(mathrm O!left(n^2log n ight))的,其实合法的((l,r))对只有(mathrm O(n))个,跟线段树类似,而且对于每个合法的((l,r))都只有一个合法的(x)对应。所以空间复杂度(mathrm O(n)),时间复杂度类似归并树,是(mathrm O!left(sum nlog n ight))的。然鹅现场我没有考虑到,于是空间开了(mathrm O(nlog n)),清空数组的时候也花了这么多。不管了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200000,LET=20;
    int n;
    char a[N+5];
    int dp[N<<2][LET];
    int dfs(int l=1,int r=n,int c=0,int p=1){//记忆化搜索 
    	if(l==r)return a[l]!='a'+c;
    	if(~dp[p][c])return dp[p][c]; 
    	int &res=dp[p][c],mid=l+r>>1,sum1=0,sum2=0;
    	for(int i=l;i<=mid;i++)sum1+=a[i]!='a'+c;
    	for(int i=mid+1;i<=r;i++)sum2+=a[i]!='a'+c;
    	res=min(sum1+dfs(mid+1,r,c+1,p<<1|1),sum2+dfs(l,mid,c+1,p<<1));//转移方程 
    //	printf("dp[%d][%d][%d]=%d
    ",l,r,c,res);
    	return res;
    }
    void mian(){
    	cin>>n;
    	scanf("%s",a+1);
    	for(int i=1;i<=4*n;i++)for(int j=0;j<LET;j++)dp[i][j]=-1;//清空 
    	cout<<dfs()<<"
    ";
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    E - Directing Edges

    CF题目页面传送门

    有一张(n)个点(m)条边的混合图,你需要给所有无向边定向,使得最终得到的有向图无环。或报告无解。本题多测。

    (sum ninleft[2,2 imes10^5 ight],sum minleft[1,2 imes10^5 ight])

    事实证明,这是本场最难的一题,因为我到最后才想出来

    首先注意到,若不考虑所有无向边,剩下来的有向图是有环的,那显然无解。

    然后呢?当时顺序做到这题的时候我想了DFS,Tarjan缩点(这个一年没写了,我已经不会了),都没有思路,却没有考虑到有向图上经常用到的拓扑排序(最后才想到)。考虑对不考虑所有无向边得到的有向图拓扑排序,那么无向边的定向方案很容易构造:拓扑序小的连向拓扑序大的即可保证无环。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    const int N=200000;
    int n,m;
    vector<pair<int,int> > nei[N+1];
    int ideg[N+1];
    vector<int> topo;
    int id[N+1];
    void toposort(){//拓扑排序 
    	topo.clear();
    	queue<int> q;
    	for(int i=1;i<=n;i++)if(!ideg[i])q.push(i);
    	while(q.size()){
    		int x=q.front();
    		q.pop();
    		topo.pb(x);
    		for(int i=0;i<nei[x].size();i++){
    			int y=nei[x][i].X,z=nei[x][i].Y;
    			if(z)/*不考虑无向边*/if(!--ideg[y])q.push(y);
    		}
    	}
    }
    void mian(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)ideg[i]=0,nei[i].clear();
    	while(m--){
    		int x,y,z;
    		scanf("%d%d%d",&z,&x,&y);
    		if(z)nei[x].pb(mp(y,z)),ideg[y]++;
    		else nei[x].pb(mp(y,z)),nei[y].pb(mp(x,z));
    	}
    	toposort();
    	if(topo.size()!=n)return puts("NO"),void();//无解 
    	puts("YES");
    	for(int i=0;i<n;i++)id[topo[i]]=i;
    	for(int i=1;i<=n;i++)for(int j=0;j<nei[i].size();j++){
    		int x=nei[i][j].X,y=nei[i][j].Y;
    		if(y)printf("%d %d
    ",i,x);
    		else if(id[i]<id[x])printf("%d %d
    ",i,x);//拓扑序小的连向拓扑序大的 
    	}
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    F - Removing Leaves

    CF题目页面传送门

    给定一棵无根树(T=(V,E),|V|=n,|E|=n-1)和一个常数(m)。每次可以选择恰好(m)个唯一连接的节点相同的叶子并删除。求最多能删多少次。本题多测。

    (sum ninleft[1,2 imes 10^5 ight])

    我记得我在这篇里吐槽过打的3次D3F全是树形DP?这次树形DP喜加一。

    注意到,对于任何一种删的方案,最终必定会有至少一个节点留下来删不掉。我们可以钦定这个点为根树形DP,最后二次扫描。接下来考虑如何DP。

    (dp0_i)表示子树(i)是否能删得只剩一个(i)(dp_i)表示子树(i)内最多能删几次。转移挺简单的,设(cnt_i=sumlimits_{jin son_i}dp0_j),则

    [egin{cases}dp0_i=[cnt_i=|son_i|][mmid cnt_i]\dp_i=sumlimits_{jin son_i}dp_j+leftlfloordfrac{cnt_i}m ight floorend{cases} ]

    接下来就愉快地做出来了。

    然而现场我nt了。我就懒得写严格(mathrm O(1))的换根,写了个calc函数计算DP值((mathrm O(|son_x|))),然后换根的时候调用这个函数。我当时zz地认为这样是均摊(mathrm O(1))的,交上去,TLE13。我就很生气,CF啥时候也卡常了?就算卡,这也要卡?于是吸臭氧码读优还是T。无奈之下只好改成严格(mathrm O(1))的换根,这样还需要记录(cnt)数组。然后就A了。赛后才发现一个菊花图就能把我卡没了。。。。。

    代码:

    #pragma GCC optimize(3)///xk
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    void read(int &x){///xk
    	x=0;char c=getchar();
    	while(!isdigit(c))c=getchar();
    	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    void prt(int x){///xk
    	if(x>9)prt(x/10);
    	putchar(x%10^48);
    }
    const int N=200000;
    int n,m;
    vector<int> nei[N+1];
    int cnt[N+1];
    bool dp0[N+1];
    int dp[N+1];
    void dfs(int x=1,int fa=0){//初DP 
    	cnt[x]=0;dp0[x]=true;dp[x]=0;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa)continue;
    		dfs(y,x);
    		cnt[x]+=dp0[y];dp0[x]&=dp0[y];dp[x]+=dp[y];
    	}
    	dp[x]+=cnt[x]/m;
    	dp0[x]&=cnt[x]%m==0;
    //	printf("dp[%d]=%d
    ",x,dp[x]);
    }
    int ans;
    void dfs0(int x=1,int fa=0){//二次扫描 
    //	printf("%d=%d
    ",x,dp[x]);
    	ans=max(ans,dp[x]);//更新答案 
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa)continue;
    		int cnt_x=cnt[x],dp0_x=dp0[x],dp_x=dp[x],cnt_y=cnt[y],dp0_y=dp0[y],dp_y=dp[y];
    		cnt[x]-=dp0[y];dp0[x]=cnt[x]==nei[x].size()-1&&cnt[x]%m==0;dp[x]=dp[x]-dp[y]-(cnt[x]+dp0[y])/m+cnt[x]/m;
    		cnt[y]+=dp0[x];dp0[y]=cnt[y]==nei[y].size()&&cnt[y]%m==0;dp[y]=dp[y]+dp[x]-(cnt[y]-dp0[x])/m+cnt[y]/m;//换根 
    		dfs0(y,x);
    		cnt[x]=cnt_x;dp0[x]=dp0_x;dp[x]=dp_x;cnt[y]=cnt_y;dp0[y]=dp0_y;dp[y]=dp_y;//还原 
    	}
    }
    void mian(){
    	read(n);read(m);
    	for(int i=1;i<=n;i++)nei[i].clear();
    	for(int i=1;i<n;i++){
    		int x,y;
    		read(x);read(y);
    		nei[x].pb(y);nei[y].pb(x);
    	} 
    	dfs();
    	ans=0;dfs0();
    	prt(ans);putchar('
    ');
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    G - Columns Swaps

    CF题目页面传送门

    有一个(2 imes n)的矩阵(a),每个数在([1,n])内。要求若干次交换某一列的(2)个值,使得最后每行都是(1sim n)的排列。求最小次数以及对应方案,或报告无解。本题多测。

    (sum nin[1,2 imes 10^5])

    首先,每个数出现的次数必须要是(2),否则无解。

    然后。看到排列想到图论。不难发现结论:若(forall iin[1,n]),连有向边((a_{1,i},a_{2,i})),得到的图可以表示为一个排列当且仅当上下都是排列。证明的话,必要性可以用置换的乘积证,充分性xjb随便证即可(就是每个点入出度都为(1),则在上下各出现过一次)。

    那么,原操作相当于将一条边反向。考虑将原(a)的图建出来,不考虑方向依次考虑每个CC(环),然后可以整体调成(2)种方向,比个大小即可。对于CC大小为(1)(2)需要讨论一下,有点烦?(当时差点讨论绝望了)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    const int N=200000;
    int n;
    int a[N+1],b[N+1];
    vector<pair<int,pair<int,int> > > nei[N+1];
    int cnt[N+1];
    bool vis[N+1];
    vector<int> zero,one;
    int st,to;
    void dfs(int x){//找环 
    	vis[x]=true;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i].X,z=nei[x][i].Y.X,xx=nei[x][i].Y.Y;
    		if(!vis[y]){
    			if(x==st)to=y;
    			dfs(y);
    			if(xx)one.pb(z);else zero.pb(z);
    		}
    	}
    }
    void mian(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)cnt[i]=0,nei[i].clear(),vis[i]=false;
    	for(int i=1;i<=n;i++)scanf("%d",a+i),cnt[a[i]]++;
    	for(int i=1;i<=n;i++)scanf("%d",b+i),cnt[b[i]]++;
    	for(int i=1;i<=n;i++)if(cnt[i]!=2)return puts("-1"),void();//判无解 
    	for(int i=1;i<=n;i++)nei[a[i]].pb(mp(b[i],mp(i,1))),nei[b[i]].pb(mp(a[i],mp(i,0)));//建图 
    	vector<int> ans;
    	for(int i=1;i<=n;i++)if(!vis[i]){
    		one.clear(),zero.clear(),st=i,dfs(i);
    		if(one.empty()&&zero.empty())continue;//大小为1 
    		if(nei[st][0].X==nei[st][1].X){//大小为2 
    			if(nei[st][0].Y.Y==nei[st][1].Y.Y)ans.pb(nei[st][0].Y.X);
    			continue;
    		}
    		int id=nei[st][0].X==to?1:0;
    		if(nei[st][id].Y.Y)zero.pb(nei[st][id].Y.X);else one.pb(nei[st][id].Y.X);
    		if(one.size()<zero.size())for(int i=0;i<one.size();i++)ans.pb(one[i]);
    		else for(int i=0;i<zero.size();i++)ans.pb(zero[i]);//比大小压进答案序列 
    	}
    	printf("%d
    ",int(ans.size()));
    	for(int i=0;i<ans.size();i++)printf("%d ",ans[i]);
    	puts("");
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    
  • 相关阅读:
    Spring事务原理一探
    浅谈AI视频技术超分辨率
    网易云信独家技术支持,壹点灵领跑心理服务行业
    音视频技术“塔尖”之争,网易云信如何C位出道?
    浅析为何使用融合CDN是大趋势?
    谈谈接入各种第三方推送平台的技术方案和一点经验
    编解码器之战:AV1、HEVC、VP9和VVC
    三年深入探索,网易云信让在线医疗做到技术“在线”
    5分钟学会Java9-Java11的七大新特性
    网易云信案例简析:锤科情怀缩影,子弹短信路在何方?
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-1385.html
Copyright © 2011-2022 走看看