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

12 篇文章 0 订阅
9 篇文章 0 订阅

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

  • 如果连接的新边使图构成一个偶环的话肯定就不行了,也就是连接两点的树上路径长度不能为奇数。

  • 而且就算加进来是一个奇环,两个奇环相交后也能形成一个偶环,这也不行。

  • 转化一下,也就是说我们需要加最大权值的边使原树变成一颗仙人掌。

  • 看到一个点的度数不超过 10 10 10 ,我们考虑 DP 。

  • f [ i ] [ S ] f[i][S] f[i][S] 表示以 i i i 为根的子树且不考虑其一些儿子(其集合为 S S S)的最大权值。

  • 为了顺畅转移,我们将形成奇环(即可能行的)的新边挂在其两端点 x , y x,y x,y 的 Lca 处。

  • 我们 dfs 到一个点时就先处理完其子树的 f f f ,之后就能将挂在该点上的新边拿出来更新答案。

  • 先赋初值: f [ x ] [ i ] = ∑ j ∉ i f [ j ] [ 0 ] f[x][i]=\sum_{j∉i}f[j][0] f[x][i]=j/if[j][0]

  • 对于一对 x , y x,y x,y(其 Lca 为 z z z) ,转移将会是: f [ x ] [ i ] = m a x { f [ x ] [ i   ∣   2 x ′   ∣   2 y ′ ] + s u m } f[x][i]=max\{f[x][i\ |\ 2^{x'}\ |\ 2^{y'}]+sum\} f[x][i]=max{f[x][i  2x  2y]+sum}

  • 这里的 x ′ , y ′ x',y' x,y 分别是 x , y x,y x,y z z z 路径上最接近 z z z 的点(即 z z z 的两个儿子节点)。

  • 而选了 x , y x,y x,y 后其到 z z z 的路径上就不能再选新边了,于是我们统计一个 s u m sum sum 记录产生的转移值。

  • 首先有 s u m + = C sum+=C sum+=C (即该路径的权值)。

  • 接着又有: s u m + = f [ x ] [ 0 ] + f [ y ] [ 0 ] sum+=f[x][0]+f[y][0] sum+=f[x][0]+f[y][0] x , y x,y x,y 下面都能选)

  • 然后 x , y x,y x,y z z z 路径上侧链点的值也要统计上,大概: s u m + = f [ f a [ p ] ] [ f a [ p ] 除 p 外 其 他 儿 子 ] sum+=f[fa[p]][fa[p]除p外其他儿子] sum+=f[fa[p]][fa[p]p]

  • 统计时要特判 x = z x=z x=z y = z y=z y=z 的情况。

  • 时间复杂度 O ( n m + 2 10 ∗ n ) O(nm+2^{10}*n) O(nm+210n)

  • 由于数据的一些问题,有某些点的度数竟达到了12,故以下程序中开到了 2 12 2^{12} 212

Code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cctype>
using namespace std;
const int N=1005,M=1<<12;
struct data
{
	int x,y,z;
}a[N<<2];
int tot,num,ans;
int first[N],nex[N<<1],en[N<<1];
int dep[N],fa[N][10],p[13];
int f[N][M],val[N],bel[13];
vector<int>ss[N];
inline int read()
{
	int X=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
inline void insert(int x,int y)
{
	nex[++tot]=first[x];
	first[x]=tot;
	en[tot]=y;
}
inline int max(int x,int y)
{
	return x>y?x:y;
}
void dfs(int x)
{
	dep[x]=dep[fa[x][0]]+1;
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x][0])
		{
			fa[en[i]][0]=x;
			dfs(en[i]);
		}
}
inline int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=log2(dep[x]);i>=0;i--)
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if(x==y) return x;
	for(int i=log2(dep[x]);i>=0;i--)
		if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
void dfs1(int x)
{
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x][0]) dfs1(en[i]);
	tot=0;
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x][0])
		{
			bel[tot]=en[i];
			val[en[i]]=p[tot++];
		}
	for(int i=0;i<p[tot];i++)
	{
		int sum=0;
		for(int j=0;j<tot;j++)
			if(!(i&p[j])) sum+=f[bel[j]][0];
		f[x][i]=sum;
	}
	for(int i=0;i<(int)ss[x].size();i++)
	{
		int id=ss[x][i],px=0,py=0;
		int xx=a[id].x,yy=a[id].y,sum=a[id].z;
		if(xx^x)
		{
			sum+=f[xx][0];
			for(px=xx;fa[px][0]^x;px=fa[px][0]) sum+=f[fa[px][0]][val[px]];
		}
		if(yy^x)
		{
			sum+=f[yy][0];
			for(py=yy;fa[py][0]^x;py=fa[py][0]) sum+=f[fa[py][0]][val[py]];
		}
		for(int j=0;j<p[tot];j++)
			if(!(j&val[px]) && !(j&val[py]))
				f[x][j]=max(f[x][j],f[x][j|val[px]|val[py]]+sum);
	}
}
int main()
{
	freopen("zujijihua.in","r",stdin);
	freopen("zujijihua.out","w",stdout);
	int n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read(),z=read();
		if(!z)
		{
			insert(x,y);
			insert(y,x);
		}else
		{
			a[++num]=(data){x,y,z};
			ans+=z;
		}
	}
	dfs(1);
	for(int i=p[0]=1;i<13;i++) p[i]=p[i-1]<<1;
	for(int j=1;j<10;j++)
		for(int i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	for(int i=1;i<=num;i++)
	{
		int x=a[i].x,y=a[i].y,z=lca(x,y);
		if((dep[x]+dep[y]-dep[z]*2)%2==0) ss[z].push_back(i);
	}
	dfs1(1);
	printf("%d",ans-f[1][0]);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值