JZOJ-senior-5939. 【NOIP2018模擬10.30】阻擊計劃
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
- 表示以 為根的子樹中,覆蓋了 集合中的邊時能保留的最大的答案
- 對於每條以 為 的非樹邊 ,它的答案由三部分組成
- part 1:
- part 2:以 為根的子樹的答案
- part 3: 到他們的 的路徑上不包括當前路徑的答案
- 為了更方便地維護這些資訊
- 預處理出以每個點為 的非樹邊有哪些
- 用 表示以 為根的子樹的最大的答案
- 對於每個點最多會有 種狀態
- 對於每條非樹邊,它們轉移的時候會走暴力一遍到 的路徑
- 並對 的狀態更新一遍
- 總時間複雜度為
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]);
}