您的位置:首页 > 其它

[雅礼集训6-23] T1 电报

2017-06-23 21:57 155 查看
每个点都只连向一个点,所以最终的图就是若干个基环树,原问题等价于在基环树中选出若干条互不相交的链,使链顶权值和最小,对每个基环树DP即可。

树型DP:设dp[i]表示点i及其子树中的答案,mx[i]表示max{c[son]},son为i的儿子,那么dp[i]=∑(dp[son]-mx[i])+c[i]。复杂度O(n)

环上DP:先枚举环的起始点s,且s->pre(c)的边断掉。设f[i]为s~i的答案和,那么f[i]=min{f[i-1],f[pre(i)]+mx[pre(i)]-c[i]}+dp[i],前一种情况表示从i和pre(i)的边断掉,即从i处重开一条链;后一种情况表示不断,那么pre(i)的子树就要与环上的主链断开。复杂度O(n^2)。还可以优化到O(n)。

O(n^2)做法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=100100;
int n,top,to[maxn],huan[maxn];
bool visit[maxn],pd,b[maxn];
ll ans=0,now,minf,c[maxn],dp[maxn],mx[maxn],f[maxn];
struct edge
{
int t;
ll w;
edge *next;
}*con[maxn];
bool tepan()
{
memset(visit,0,sizeof(visit));
int p=1;
while(!visit[p]){visit[p]=1;p=to[p];}
for(int i=1;i<=n;i++)
if(!visit[i]) return 0;
return 1;
}
void ins(int x,int y)
{
edge *p;
p=new edge;
p->t=y;
p->next=con[x];
con[x]=p;
}
void find(int v)
{
visit[v]=1;
if(!visit[to[v]]) find(to[v]);
else pd=1;
if(pd) {huan[++top]=v;b[v]=1;}
if(to[huan[1]]==v) pd=0;
visit[v]=0;
}
void dfs(int v)
{
visit[v]=1;
mx[v]=0;
dp[v]=c[v];
for(edge *p=con[v];p!=NULL;p=p->next)
if(!b[p->t])
{
dfs(p->t);
mx[v]=max(mx[v],c[p->t]);
dp[v]+=dp[p->t];
}
dp[v]-=mx[v];
}
int p(int x){return (x==1)?top:(x-1);}
int s(int x){return (x==top)?1:(x+1);}

int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%lld",&to[i],&c[i]);
ins(to[i],i);
}
if (tepan()) ans=0;
else
{
memset(b,0,sizeof(b));
memset(dp,0,sizeof(dp));
memset(visit,0,sizeof(visit));
for(int i=1;i<=n;i++)
if(!visit[i])
{
top=0;
now=1e15;
find(i);
for(int j=1;j<=top;j++)
dfs(huan[j]);
for(int j=1;j<=top;j++)
{
f[j]=dp[huan[j]];
for(int k=s(j);k!=j;k=s(k))
f[k]=min(f[p(k)]+dp[huan[k]],f[p(k)]+mx[huan[p(k)]]+dp[huan[k]]-c[huan[k]]);
now=min(now,f[p(j)]);
}
ans+=now;
}

}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: