zoukankan      html  css  js  c++  java
  • 【LOJ#3026】管道监控

    题目

    题目链接:https://loj.ac/p/3026

    (nleq 500,mleq 10^5,sum |s_i|leq 10^6)

    思路

    我们可以把字符串倒着插入一棵 Trie,然后枚举给出的树上的所有点 (x),同时在这个节点和 Trie 树上跳。如果当前跳到点 (y),此时 Trie 树上跳到的点恰好是某一个字符串的结尾,那么我们就从 (y)(x) 连一条流量为 (1),费用为匹配的字符串的代价的边。
    然后我们设点 (i) 子树内有 (f_i) 个叶子,那么就从 (i) 的父亲向 (i) 连一条流量为 (f_i),费用为 (0) 的边,同时从 (i)(i) 的父亲连一条流量为 (+infty),费用为 (0) 的边。
    最后从源点向 (1) 连流量为 (+infty),费用为 (0) 的边,每一个叶子向汇点连流量为 (1),费用为 (0) 的边,跑最小费用最大流即可。
    这个思路和昨天比赛志愿者招募是几乎一样的。不再赘述。

    代码

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    typedef long long ll;
    
    const int N=510,M=1000010;
    const ll Inf=1e18;
    int n,m,t,S,T,tot=1,head[N],fa[N],flw[N],pre[N];
    ll cost,maxf,dis[N];
    char s[M],c[N];
    bool vis[N];
    
    struct edge
    {
    	int next,to,flow,cost,id;
    }e[M];
    
    void add(int from,int to,int flow,int cost,int id=0)
    {
    	e[++tot]=(edge){head[from],to,flow,cost,id};
    	head[from]=tot;
    	swap(from,to);
    	e[++tot]=(edge){head[from],to,0,-cost,id};
    	head[from]=tot;
    }
    
    struct Trie
    {
    	int tot,ch[M][26],cost[M],id[M];
    	Trie() { memset(cost,0x3f3f3f3f,sizeof(cost)); tot=1; }
    	
    	void ins(char *s,int val,int k)
    	{
    		int p=1,len=strlen(s+1);
    		for (int i=len;i>=1;i--)
    		{
    			if (!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++tot;
    			p=ch[p][s[i]-'a'];
    		}
    		if (val<cost[p]) cost[p]=val,id[p]=k;
    	}
    	
    	void addedge(int x)
    	{
    		int p=1;
    		for (int y=x;fa[y];y=fa[y])
    		{
    			if (!ch[p][c[y]-'a']) break;
    			p=ch[p][c[y]-'a'];
    			if (cost[p]<Inf) add(fa[y],x,1,cost[p],id[p]);
    		}
    	}
    }trie;
    
    bool spfa()
    {
    	memset(dis,0x3f3f3f3f,sizeof(dis));
    	deque<int> q;
    	q.push_back(S); dis[S]=0;
    	while (q.size())
    	{
    		int u=q.front(); q.pop_front();
    		vis[u]=0;
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (e[i].flow && dis[v]>dis[u]+e[i].cost)
    			{
    				dis[v]=dis[u]+e[i].cost; pre[v]=i;
    				if (!vis[v])
    				{
    					vis[v]=1;
    					if (q.size() && dis[v]<=dis[q.front()]) q.push_front(v);
    						else q.push_back(v);
    				}
    			}
    		}
    	}
    	return dis[T]<Inf;
    }
    
    void addflow()
    {
    	int minf=Inf;
    	for (int i=T;i!=S;i=e[pre[i]^1].to)
    		minf=min(minf,e[pre[i]].flow);
    	for (int i=T;i!=S;i=e[pre[i]^1].to)
    		e[pre[i]].flow-=minf,e[pre[i]^1].flow+=minf;
    	cost+=dis[T]*minf; maxf-=minf;
    }
    
    void MCMF()
    {
    	while (spfa()) addflow();
    }
    
    signed main()
    {
    	memset(head,-1,sizeof(head));
    	S=N-1; T=N-2;
    	scanf("%lld%lld%lld",&n,&m,&t);
    	for (int i=2;i<=n;i++)
    	{
    		scanf("%lld",&fa[i]);
    		while (c[i]=getchar())
    			if (c[i]>='a' && c[i]<='z') break;
    		flw[i]=1; flw[fa[i]]=0;
    	}
    	add(S,1,Inf,0);
    	for (int i=1;i<=n;i++)
    		if (flw[i]) add(i,T,1,0),maxf++;
    	for (int i=n;i>=1;i--)
    	{
    		add(fa[i],i,flw[i]-1,0);
    		add(i,fa[i],Inf,0);
    		flw[fa[i]]+=flw[i];
    	}
    	for (int i=1,x;i<=m;i++)
    	{
    		scanf("%lld%s",&x,s+1);
    		trie.ins(s,x,i);
    	}
    	for (int i=1;i<=n;i++)
    		trie.addedge(i);
    	MCMF();
    	if (maxf) return printf("-1"),0;
    	cout<<cost<<"
    ";
    	if (t)
    	{
    		cost=0;
    		for (int i=2;i<=tot;i+=2)
    			if (!e[i].flow && e[i].id) cost++;
    		cout<<cost<<"
    ";
    		for (int i=2;i<=tot;i+=2)
    			if (!e[i].flow && e[i].id)
    				cout<<e[i^1].to<<" "<<e[i].to<<" "<<e[i].id<<"
    ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    java获取客户端用户真实ip
    electron制作上位机软件篇(三)启动项目并进行打包
    electron制作上位机软件篇(二)使用serialport进行串口通信
    electron制作上位机软件篇(一):编译安装serialport
    STM32学习篇-蜂鸣器
    STM32学习篇-跑马灯
    日志级别的选择:Debug、Info、Warn、Error还是Fatal
    常用的正则验证
    kindEditor富文本编辑器
    表单验证jq.validate.js
  • 原文地址:https://www.cnblogs.com/stoorz/p/15024640.html
Copyright © 2011-2022 走看看