JZOJ-senior-5939. 【NOIP2018模拟10.30】阻击计划

3 篇文章 0 订阅
3 篇文章 0 订阅
Time Limits: 1000 ms Memory Limits: 262144 KB

Description

最近,小J发现小R和小Z之间的关系十分密切,心中十分嫉妒,为了拆散他们,小J经常扰乱他们一起玩耍的计划。

小R和小Z打算在这个周末一起骑车在G国的城市看风景,G国的城市有n个城市,m条双向道路,这m条边中,有n-1条道路已经铺设完毕,任意两个城市之间都有一条由铺设好的道路组成的路径。

由于G国经常收到周围强大力场的影响,G国的每个城市至多是十条道路的端点(包括铺设好和未铺设好的道路)。

小R和小Z制订了这样一个Van耍计划:从一个城市开始,沿着G国的道路骑行,途中不经过之前已经去过的城市,也不经过之前去过的道路,最后回到起点城市。

由于他们骑得是双人自行车,前排的座位比后排的作为更累,他们决定每次到达一个城市都会换一次位置,为了保证每个人的体力消耗相同,继续进行他们下面的游戏,他们需要一条恰好有偶数条道路的路径。

为了阻止他们,小J决定破坏一些没有被铺设好的道路,由于自身能力不足,他找到了你,并将自己一周的研究数据——破坏每条未被铺设好的道路的花费告诉了你,希望你帮他算出他至少需要花费多少代价才能阻止小R和小Z的计划。

Input

第一行两个正整数n,m表示G国的城市数和道路数

接下来m行,每行三个整数A,B,C,表示G国的一条道路,从A出发到B,破坏它的代价为C(未经铺设的道路C值一定不为0),由于小J智商有限,已经铺设好的道路他不能毁坏,也就失去了侦察的必要,他们的花费为0

Output

一行一个整数表示小J的最小花费

Sample Input

Sample Input1:
5 8
2 1 0
3 2 0
4 3 0
5 4 0
1 3 2
3 5 2
2 4 5
2 5 1

Sample Input2:
9 14
1 2 0
1 3 0
2 3 14
2 6 15
3 4 0
3 5 0
3 6 12
3 7 13
4 6 10
5 6 0
5 7 0
5 8 0
6 9 11
8 9 0

Sample Output

Sample Output1:
5

样例说明
破坏道路1-3,3-5,2-5

Sample Output2:
48

Data Constraint

对于5%的数据,任何一条未经铺设的道路两端都有一条直接连着他们的铺设好的道路
对于另外10%的数据,最多只有10条未被铺设的道路
对于另外15%的数据,最多只有21条未被铺设的道路
对于上述两档部分分,数据有梯度
对于另外30%的数据,已经铺设好的道路构成一条链
对于所有数据 n≤1000,m≤5000,每条道路的花费≤10000

Solution

  • 题目大意:给出一棵树和非树边,用最小的代价删去一些非树边,使得图中不存在偶环
  • 转化一下,题意即为在树上保留尽可能多的奇环,使得保留的权值最大
  • 如果一条非树边树上路径长度为奇数,那么它一定是个偶环,先把这样的边删去
  • 考虑两个奇环,如果它们在树上部分有交,那一定有方法走成偶环,例如下图
  • 原本走完两个环为偶数,再同时少走了交集那一部分的两倍(也是偶数)
  • 所以一定存在偶环(偶-2*偶=偶)
    在这里插入图片描述
  • 所以说,留下来的部分中任意一条边都不可能同时属于两个简单环,即留下的为仙人掌
  • (此处不考虑早已删去的非树边)
  • 如果让每条非树边覆盖他连接两点树上的路径上的所有边
  • 那么树上的每一条边最多只能被覆盖一次
  • 再将必须删除的最小边权改为总边权-能留下的最大边权
  • 考虑到每个城市至多是十条道路的端点,用壮压DP
  • F i , s F_{i,s} Fi,s 表示以 i i i 为根的子树中,覆盖了 s s s 集合中的边时能保留的最大的答案
  • 对于每条以 i i i l c a lca lca 的非树边 ( x , y ) (x,y) (x,y),它的答案由三部分组成
  • part 1: w ( x , y ) w(x,y) w(x,y)
  • part 2:以 x , y x,y x,y 为根的子树的答案
  • part 3: x , y x,y x,y 到他们的 l c a lca lca 的路径上不包括当前路径的答案
  • 为了更方便地维护这些信息
  • 预处理出以每个点为 l c a lca lca 的非树边有哪些
  • h [ x ] h[x] h[x] 表示以 x x x 为根的子树的最大的答案
  • 对于每个点最多会有 2 10 2^{10} 210 种状态
  • 对于每条非树边,它们转移的时候会走暴力一遍到 l c a lca lca 的路径
  • 并对 l c a lca lca 的状态更新一遍
  • 总时间复杂度为 O ( m ∗ ( 2 10 + n ) ) O(m*(2^{10}+n)) O(m(210+n))

Code

#include<algorithm>
#include<cstdio>
#include<vector>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)

using namespace std;

const int N=1e3+5,M=5e3+5,K=15;
int n,m,sum,num,tot;
int _2[K],dep[N],last[N],ff[N][K];
int f[N][1<<K],h[N],q[N];
int gs[N],id[N],son[N][K];
struct edge{int to,next,v;}e[2*N];
struct node{int x,y,z;}b[M];
struct path{int x,w;};
vector<int> V[N];

void link(int x,int y,int z)
{
	e[++num]=(edge){y,last[x],z},last[x]=num;
}

void dfs(int x,int fa)
{
	ff[x][0]=fa,dep[x]=dep[fa]+1;
	for(int w=last[x];w;w=e[w].next)
		if(e[w].to!=fa) dfs(e[w].to,x);
}

int lca(int x,int y)
{
	fd(i,14,0) if(dep[ff[x][i]]>=dep[y]) x=ff[x][i];
	if(x==y) return x;
	fd(i,14,0) if(ff[x][i]!=ff[y][i]) x=ff[x][i],y=ff[y][i];
	return ff[x][0];
}

path road(int x,int z)
{
	if(x==z) return (path){0,0};
	int s=h[x];
	while(ff[x][0]!=z)
	{
		int fa=ff[x][0];
		s+=f[fa][(_2[gs[fa]]-1)^_2[id[x]-1]];
		x=fa;
	}
	return (path){x,s};
}

void update0(int x)
{
	fo(s,0,_2[gs[x]]-1)
	{
		int w=0;
		fo(j,1,gs[x]) if(s&_2[j-1]) w+=h[son[x][j]];
		f[x][s]=max(f[x][s],w);
		h[x]=max(h[x],f[x][s]);
	}
}

void update1(int z,int x,int y,int w)
{
	int ss;
	if(!x) ss=_2[id[y]-1];
		else if(!y) ss=_2[id[x]-1];
			else ss=_2[id[x]-1]+_2[id[y]-1];
	fd(s,_2[gs[z]]-1,0) if((s&ss)==0)
	{
		f[z][s|ss]=max(f[z][s|ss],f[z][s]+w);
		h[z]=max(h[z],f[z][s|ss]);
	}
}

void calc(int k,int z)
{
	path a1=road(b[k].x,z);
	path a2=road(b[k].y,z);
	int w=b[k].z+a1.w+a2.w;
	update1(z,a1.x,a2.x,w);
}

void dg(int x,int fa)
{
	for(int w=last[x];w;w=e[w].next)
	{
		int y=e[w].to;
		if(y==fa) continue;
		id[y]=++gs[x];
		son[x][gs[x]]=y;
		dg(y,x);
	}
	update0(x);
	fo(j,0,q[x]-1) calc(V[x][j],x);
}

int main()
{
	freopen("zujijihua.in","r",stdin);
	freopen("zujijihua.out","w",stdout);
	_2[0]=1; fo(i,1,14) _2[i]=_2[i-1]<<1;
	scanf("%d%d",&n,&m);
	fo(i,1,m)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z),sum+=z;
		if(x==y) continue;
		if(!z) link(x,y,z),link(y,x,z);
			else b[++tot]=(node){x,y,z};
	}
	dfs(1,0);
	fo(j,1,14)
		fo(i,1,n)
			ff[i][j]=ff[ff[i][j-1]][j-1];
	fo(i,1,tot)
	{
		int x=b[i].x,y=b[i].y;
		if(dep[x]<dep[y]) swap(x,y),swap(b[i].x,b[i].y);
		if((dep[x]-dep[y])&1) continue;
		int z=lca(x,y);
		V[z].push_back(i),++q[z];
	}
	dg(1,0);
	printf("%d",sum-h[1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值