您的位置:首页 > 其它

[GDOI2017模拟9.4]同桌的你

2016-09-04 13:06 232 查看

Description



Input



Output



Data Constraint



分析

对每个环套树分别求答案。

考虑一棵树上怎样求答案:设f[i][0/1]表示以i为根的子树,其中1表示i与它的某个儿子配对(0表示没有),最优解是多少。这是一个O(n)的dp。

现在考虑上环,如果暴力地把每条边分别删去,然后求答案,是很慢的。

但是注意到,匹配是对于相邻的两个点的,也就是说选取了一条边后,相邻的边都不能取。可以在环上任意找一条边,删去后做一次dp,得到的就是不选取这条边所连接的两个点的答案。

然后把它相邻的一条边删去,把这条边还原,就可以把选取这条边的答案算进来。

时间复杂度O(n)

输出方案就标记一下这个点和哪个儿子配对即可。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=1000005;

typedef long long LL;

int n,t,tot,a[maxn],b[maxn],h[maxn],e[maxn],next[maxn],f[maxn][2][2],son[maxn],st[maxn],g[maxn];

int ans1,ans2,data[maxn],se[maxn],now[maxn],p0,p1;

bool visit[maxn],bz[maxn];

char c,cc[10];

int read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
int x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}

void write(int x)
{
if (x==0)
{
putchar('0'); return;
}
int len=0;
for (;x;x/=10) cc[len++]=x%10+48;
for (int i=len-1;i>=0;i--) putchar(cc[i]);
}

void add(int x,int y)
{
e[++tot]=y; next[tot]=h[x]; h[x]=tot;
}

void dp(int i)
{
int j,k,x,s1,s0,n0,n1,s;
data[tot=1]=i;
for (j=1;j<=tot;j++)
{
x=data[j]; visit[x]=1;
g[x]=0;
memset(f[x][1],0,sizeof(f[x][1]));
for (k=h[x];k;k=next[k]) if (e[k]!=i) data[++tot]=e[k];
}
for (j=tot;j;j--)
{
x=data[j];
s1=s0=s=bz[x]=0;
for (k=h[x];k;k=next[k]) if (e[k]!=i)
{
son[s++]=e[k];
s0+=f[e[k]][bz[e[k]]][0]; s1+=f[e[k]][bz[e[k]]][1];
}
f[x][0][0]=s0; f[x][0][1]=s1;
for (k=0;k<s;k++)
{
n0=s0-f[son[k]][bz[son[k]]][0]+f[son[k]][0][0]+1;
n1=s1-f[son[k]][bz[son[k]]][1]+f[son[k]][0][1]+(b[x]^b[son[k]]);
if (f[x][1][0]<n0 || f[x][1][0]==n0 && f[x][1][1]<n1)
{
f[x][1][0]=n0; f[x][1][1]=n1; g[x]=son[k];
}
}
if (f[x][1][0]>f[x][0][0] || f[x][1][0]==f[x][0][0] && f[x][1][1]>f[x][0][1]) bz[x]=1;
}
if (p0<f[i][bz[i]][0] || p0==f[i][bz[i]][0] && p1<f[i][bz[i]][1])
{
p0=f[i][bz[i]][0]; p1=f[i][bz[i]][1];
for (j=1;j<=tot;j++)
{
x=data[j];
if (bz[x])
{
now[x]=g[x];
for (k=h[x];k;k=next[k]) if (e[k]==g[x]) bz[e[k]]=0;
}else now[x]=0;
}
}
}

void work()
{
n=read();
memset(h,0,sizeof(h));
tot=0;
for (int i=1;i<=n;i++)
{
a[i]=read(); b[i]=read()-1;
add(a[i],i);
}
memset(se,0,sizeof(se));
memset(visit,0,sizeof(visit));
ans1=ans2=0;
for (int i=1;i<=n;i++) if (!visit[i])
{
int j;
for (j=i;!visit[j];j=a[j]) visit[j]=1;
p0=p1=0;
dp(j); dp(a[j]);
for (j=1;j<=tot;j++) se[data[j]]=now[data[j]];
ans1+=p0; ans2+=p1;
}
printf("%d %d\n",ans1,ans2);
for (int i=1;i<=n;i++) if (se[i])
{
write(i); putchar(' '); write(se[i]); putchar('\n');
}
}

int main()
{
for (t=read();t--;work());
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: