这个可以这样想,由于每个点都可以立刻结束回到原点,所有可以把所有节点都当成汇点,把1号点当成源点,然后直接跑费用流,然而由于要满足每个边都要跑一次的条件,直接给个下界就可以了。。然后用处理下界的方法建图就可以了。。
然而这样直接跑费用流T了(9750ms= =)。。
发现建图时容量要么是1(必要边),要么是无穷,所以可以考虑在建图上优化一下。。
处理必要边时可以先把费用加上,然后标记入度和出度之后再向源汇点连边。。这样可以合并很多重边。。
然后跑起来是196ms= =
/**
* ┏┓ ┏┓
* ┏┛┗━━━━━━━┛┗━━━┓
* ┃ ┃
* ┃ ━ ┃
* ┃ > < ┃
* ┃ ┃
* ┃... ⌒ ... ┃
* ┃ ┃
* ┗━┓ ┏━┛
* ┃ ┃ Code is far away from bug with the animal protecting
* ┃ ┃ 神兽保佑,代码无bug
* ┃ ┃
* ┃ ┃
* ┃ ┃
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━━━━━━━━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 305
#define nm 50005
#define pi 3.1415926535897931
const int inf=1e9+7;
using namespace std;
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
struct edge{int t,v,w;edge*next,*rev;}e[nm],*h[NM],*o=e,*p[NM];
void _add(int x,int y,int w,int v){o->v=v;o->w=w;o->t=y;o->next=h[x];h[x]=o++;}
void add(int x,int y,int w,int v){_add(x,y,w,v);_add(y,x,0,-v);h[x]->rev=h[y];h[y]->rev=h[x];}
int n,w[NM],d[NM],_x,_y,ans,s,a[NM];
bool v[NM];
queue<int>q;
int bfs(){
inc(i,0,n)d[i]=inf;mem(w);mem(v);
d[0]=0;w[0]=inf;q.push(0);
while(!q.empty()){
int t=q.front();q.pop();v[t]=false;
link(t)if(j->w&&d[j->t]>d[t]+j->v){
d[j->t]=d[t]+j->v;w[j->t]=min(w[t],j->w);p[j->t]=j;
if(!v[j->t])v[j->t]++,q.push(j->t);
}
}
return w[n];
}
int main(){
n=read();
inc(i,1,n){
int _=read();while(_--){
_x=read();_y=read();
ans+=_y;
add(i,_x,inf,_y);a[i]++;a[_x]--;
}
}
inc(i,2,n)add(i,1,inf,0);
inc(i,1,n)if(a[i]>0)add(i,n+1,a[i],0);else if(a[i]<0)add(0,i,-a[i],0);
n++;
while(bfs()){
ans+=d[n]*w[n];s+=w[n];
for(int x=n;x;x=p[x]->rev->t)p[x]->w-=w[n],p[x]->rev->w+=w[n];
}
//printf("%d\n",s);
return 0*printf("%d\n",ans);
}
3876: [Ahoi2014&Jsoi2014]支线剧情
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 2395 Solved: 1446
[Submit][Status][Discuss]
Description
【故事背景】
宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等。不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情。这些游戏往往
都有很多的支线剧情,现在JYY想花费最少的时间看完所有的支线剧情。
【问题描述】
JYY现在所玩的RPG游戏中,一共有N个剧情点,由1到N编号,第i个剧情点可以根据JYY的不同的选择,而经过不同的支线剧情,前往Ki种不同的新的剧情点。当然如果为0,则说明i号剧情点是游戏的一个结局了。
JYY观看一个支线剧情需要一定的时间。JYY一开始处在1号剧情点,也就是游戏的开始。显然任何一个剧情点都是从1号剧情点可达的。此外,随着游戏的进行,剧情是不可逆的。所以游戏保证从任意剧情点出发,都不能再回到这个剧情点。由于JYY过度使用修改器,导致游戏的“存档”和“读档”功能损坏了,
所以JYY要想回到之前的剧情点,唯一的方法就是退出当前游戏,并开始新的游戏,也就是回到1号剧情点。JYY可以在任何时刻退出游戏并重新开始。不断开始新的游戏重复观看已经看过的剧情是很痛苦,JYY希望花费最少的时间,看完所有不同的支线剧情。
Input
输入一行包含一个正整数N。
接下来N行,第i行为i号剧情点的信息;
第一个整数为,接下来个整数对,Bij和Tij,表示从剧情点i可以前往剧
情点,并且观看这段支线剧情需要花费的时间。
Output
输出一行包含一个整数,表示JYY看完所有支线剧情所需要的最少时间。
Sample Input
6
2 2 1 3 2
2 4 3 5 4
2 5 5 6 6
0
0
0
Sample Output
24
HINT
JYY需要重新开始3次游戏,加上一开始的一次游戏,4次游戏的进程是
1->2->4,1->2->5,1->3->5和1->3->6。
对于100%的数据满足N<=300,0<=Ki<=50,1<=Tij<=300,Sigma(Ki)<=5000
Source