【一堆题】from hsq学长的一堆构造题
见到了久闻帅气的声音不存在的hsq学长,一堆一堆很难的构造题(更新ing)
T1 : codeforces 741C
现在有2n个人按1~2n的顺序围着圆桌吃两种菜,给出n对数对(u,v),代表第u个人和第v个人是情侣。要求每对情侣都不能吃同一种菜,且每连续的3个人(注意2n与1相邻)不能都吃同一种菜。请你找出一种符合题意的上菜方案,如果无解输出-1。
我们发现其限制其实很弱,我们可以发现-->一定有解,且我们可以证明,如果将限制的人之间连边,其成环时一定边长为偶数!那么我们直接将1连2,3连4,4连5.。。。。就可以了。
剩下的就是我不会的二分图染色了orz,学习了一波orz。
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<vector> const int maxn = 200005; using namespace std; int n; int col[maxn]; vector<int>ve[maxn]; bool dfs(int x) { for(int i=ve[x].size()-1;i>=0;i--) { if(!col[ve[x][i]]) { col[ve[x][i]] = 3-col[x]; if(!dfs(ve[x][i])) return false; } else if(col[ve[x][i]]==col[x]) return false; } return true; } int cpa[maxn],cpb[maxn]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int a,b; scanf("%d%d",&a,&b); ve[a].push_back(b); ve[b].push_back(a); cpa[i]=a; cpb[i]=b; } for(int i=1;i<=n;i++) { ve[i*2-1].push_back(i*2); ve[i*2].push_back(i*2-1); } bool flag = 1; for(int i=1;i<=2*n;i++) { if(!col[i]) { col[i] = 1; if(!dfs(i)) { flag=0;break ; } } } if(flag) { for(int i=1;i<=n;i++) { printf("%d %d\n",col[cpa[i]],col[cpb[i]]); } } else printf("-1"); }CF 752F 大致题意:给定一棵n个节点的树,每个节点是一个城市,2k支球队生活在2k个不同的城市。现在要安排k场两支队伍之间的比赛,每支队伍都必须比赛,且比赛地点必须设置在两支参赛队伍所在城市之间的最短路径上,每个城市可以同时举办多场比赛。请你合理安排每支队伍的对手和比赛地点,使得可以安排尽量少的比赛地点,并输出你的方案。 大胆猜测并能发现,作为一棵树,一个比赛地点就足够了。那么我们找到的第一个比赛地点就是在队伍位置权值+1,从下往上回溯的时候加上所有子树的权值,找到第一个权值》=k的点,这就是比赛地了,之后就是以该点当根求出所有比赛地点dfs序,dfs序中i与i+k比赛就可以了。至于为什么第一个就可以了,因为他子树一定有k+1个可以互相到底并且可以到达以他为根他往上的其他<=k个点,满足题意。 本题121个点,评测了好久好久orz orz orz
#include<stdio.h> #include<bits/stdc++.h> using namespace std; const int maxn = 200005; vector<int>ve[maxn]; int n,k; int ans; int f[maxn],fa[maxn]; int dfx,dfn[maxn]; bool dui[maxn]; void dfs(int x,int ba) { if(ans) return; for(int i=ve[x].size()-1;i>=0;i--) { if(ve[x][i]==ba) continue; dfs(ve[x][i],x); if(ans) return; f[x]+=f[ve[x][i]]; } if(f[x]>=k) ans = x; return; } void dds(int x,int ba) { if(dui[x]) dfn[++dfx]=x; for(int i=ve[x].size()-1;i>=0;i--) { if(ve[x][i]==ba) continue; dds(ve[x][i],x); } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n-1;i++) { int a,b; scanf("%d%d",&a,&b); ve[a].push_back(b); ve[b].push_back(a); } for(int i=1;i<=2*k;i++) { int x; scanf("%d",&x); f[x] = dui[x] = 1; } dfs(1,0); dds(ans,0); puts("1"); printf("%d\n",ans); for(int i=1;i<=k;i++) { printf("%d %d %d\n",dfn[i],dfn[i+k],ans); } }Codeforces 758E 给定一棵有根树,每条边有两个属性:韧性pi和重量wi。对于每一条边,你可以保持p和w均非负的前提下,令p和w同时减小一个非负数。请问是否可以在操作之后使得每条边的p都不小于以该边为根的子树中w之和,如果可以则输出任意一种使得整棵树的w之和最大的方案。 我们直接将整颗树构造成使得整颗树的w之和最小的情况(从叶子结点回溯),同时判断有无解,然后再从根将可以满足的加回去(从根开始加),这样就一定能满足性质。 为什么要这样搞?因为w之和最大方案太多不好搞,w之和最小很好找出来,在这个基础上往回加就很好搞了。 (代码先存着写(由于代码能力太弱写炸了))