2021 Hubei Provincial Collegiate Programming Contest E. Revue
题目描述
n个人,每个人的初始分数不同(具体分数未知)
有m次已知的Revue(按顺序发生),每次Revue形式为(x,y),意为x打败y,之后x的分变成二者max,y变成min
现在你要按顺序在最后加入w次Revue,要保证 在所有m+w次Revue中删掉任意k(k给出)次Revue后 的 所有初始分数的可能中,1都能获得最大分值
最小化w,当w<=1000时输出方案
n,m,k<=1e6
题解
顶级思维题
实际不需要关心其他的分数,只要考虑max最后能否到1上就行
问题等价于:两个人博弈,人A加入w次操作,之后另一个人B确定max位置,同时尽可能删操作使max到不了1
(你只能决定加操作,max位置、删哪些都决定不了,所以不妨设另一个人B来按最优方式干扰你)
假设max位置在x,B删f[x]次操作就可以阻止max到A,当f[x]<=K时你只要加入K+1-f[x]个(1,x)的Revue就可以使max在x时合法,对每个x加边即可保证所有情况合法
现在问题变成了求f[x],修正/严谨一下f[x]的定义,f[x]表示在当前的Revue序列下,要让max最终留在x的最小删次数(这里的最小是所有初始max位置情况的最小)
那么f[x]初始为0(max就在x),之后每加入一个Revue(x,y)时,对于x来说,多了用f[y]次操作让max留在y,然后利用最后/新加的Revue(x,y)把max移到x,所以f[x]←max(f[x],f[y]);
同时,y如果要把max留在y,那么多了这个Revue(x,y)必删,所以f[y]←f[y]+1
然后做完了,f[x]求出后直接构造就行,还是当f[x]<=K时加K+1-f[x]次Revue
这样当B想初始把max放到合适位置,然后删f[x]次操作后到x时,要额外多删K+1-f[x]次;做不到,所以合法
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (int a=b; a<=c; a++)
#define fd(a,b,c) for (int a=b; a>=c; a--)
#define ll long long
//#define file
using namespace std;
const int N=1e6+10;
int n,m,K,T;
int f[N];
char ch;
int Read()
{
int x=0;
ch=getchar();
while (!(ch>='0' && ch<='9')) ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void solve()
{
n=Read(),m=Read(),K=Read();
fo(i,1,m)
{
int x,y;
x=Read(),y=Read();
f[x]=min(f[x],f[y]);
++f[y];
}
ll w=0;
fo(i,2,n) if (f[i]<=K) w+=K+1-f[i];
printf("%lld\n",w);
if (w<=1000)
{
fo(i,2,n)
if (f[i]<=K)
{
fo(j,1,K+1-f[i])
printf("%d %d\n",1,i);
}
}
}
int main()
{
// freopen("E.in","r",stdin);
// #ifdef file
// freopen("a.out","w",stdout);
// #endif
T=1;
//scanf("%d",&T);
for (;T;--T) solve();
return 0;
}