【一堆题】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之和最小很好找出来,在这个基础上往回加就很好搞了。
(代码先存着写(由于代码能力太弱写炸了))

浙公网安备 33010602011771号