1. 程式人生 > >暗之鏈鎖

暗之鏈鎖

是你 urn stdout space 人類 lca 前綴 pri 結構

【題目描述】

傳說中的暗之連鎖被人們稱為Dark。Dark是人類內心的黑暗的產物,古今中外的勇者們都試圖打倒它。經過研究,你發現Dark呈現無向圖的結構,圖中有N個節點和兩類邊,一類邊被稱為主要邊,而另一類被稱為附加邊。Dark有N – 1條主要邊,並且Dark的任意兩個節點之間都存在一條只由主要邊構成的路徑。另外,Dark還有M條附加邊。

你的任務是把Dark斬為不連通的兩部分。一開始Dark的附加邊都處於無敵狀態,你只能選擇一條主要邊切斷。一旦你切斷了一條主要邊,Dark就會進入防禦模式,主要邊會變為無敵的而附加邊可以被切斷。但是你的能力只能再切斷

Dark的一條附加邊。現在你想要知道,一共有多少種方案可以擊敗Dark。註意,就算你第一步切斷主要邊之後就已經把Dark斬為兩截,你也需要切斷一條附加邊才算擊敗了Dark。

【輸入格式】

第一行包含兩個整數N和M。

之後N – 1行,每行包括兩個整數A和B,表示A和B之間有一條主要邊。

之後M行以同樣的格式給出附加邊。

【輸出格式】

輸出一個整數表示答案。

【樣例輸入】

4 1

1 2

2 3

1 4

3 4

【樣例輸出】

3

【提示】

自己瞎做吧

【數據範圍】

對於20% 的數據,N≤100,M≤100。

對於100% 的數據,N≤100 000,M≤200 000。數據保證答案不超過2^31– 1。

【題解】

第一道樹上差分。差分,就是在大都市meg裏面學到的那種開頭減,結尾加,前綴和來看狀態的方法,例題還有一道借教室(不是換教室!)。在樹上差分可以表示出兩點之間的路徑,只要在兩端點均+1,LCA-2,最後用dfs統計和就可以了。過去學的LCA有樸素、ST、tarjan離線、在線、倍增,這次用的是tarjan,大概也是我最熟練的一種:兩次頭插法分別加雙向邊和存問題,還要用到並查集。差分、LCA、dfs三個步驟結束後得到每條實邊被x條虛邊覆蓋,x=0的實邊對答案有m的貢獻,x=1的實邊對答案有1的貢獻。學長們出的加強版需要刪掉不止一條虛邊一條實邊,如果邊有富裕的話就會產生一個組合數的貢獻,計算組合數大概是ad學長上次講到的那些方法,數據小遞推就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sj=200010;
int n,m,po[sj/2],a1,a2,jg,h[sj],l[sj],ans[sj],e,f;
int fa[sj/2],zx[sj/2],re[sj/2],dep[sj/2];
struct B
{
    int u,v,ne;
}b[sj];
struct Q
{
    int u,v,ne,num;
}q[sj*2];
void add(int x,int y)
{
     b[e].u
=x; b[e].v=y; b[e].ne=h[x]; h[x]=e++; } int find(int x) { if(fa[x]==-1) return x; fa[x]=find(fa[x]); return fa[x]; } void hb(int x,int y) { x=find(x); y=find(y); if(x!=y) fa[x]=y; } void lca(int x) { zx[x]=x; re[x]=1; for(int i=h[x];i!=-1;i=b[i].ne) if(!re[b[i].v]) { lca(b[i].v); hb(x,b[i].v); zx[find(b[i].v)]=x; } for(int i=l[x];i!=-1;i=q[i].ne) if(re[q[i].v]) ans[q[i].num]=zx[find(q[i].v)]; } void adq(int x,int y,int z) { q[f].u=x; q[f].v=y; q[f].num=z; q[f].ne=l[x]; l[x]=f++; } void dfs(int x) { for(int i=h[x];i!=-1;i=b[i].ne) if(!re[b[i].v]) { re[b[i].v]=1; dep[b[i].v]=dep[x]+1; dfs(b[i].v); po[x]+=po[b[i].v]; } } int main() { //freopen("t.txt","r",stdin); freopen("yam.in","r",stdin); freopen("yam.out","w",stdout); scanf("%d%d",&n,&m); memset(h,-1,sizeof(h)); memset(fa,-1,sizeof(fa)); memset(l,-1,sizeof(l)); for(int i=1;i<n;i++) { scanf("%d%d",&a1,&a2); add(a1,a2); add(a2,a1); } for(int i=1;i<=m;i++) { scanf("%d%d",&a1,&a2); adq(a1,a2,i); adq(a2,a1,i); po[a1]++; po[a2]++; } lca(1); for(int i=1;i<=m;i++) po[ans[i]]-=2; memset(re,0,sizeof(re)); re[1]=1; dep[1]=1; dfs(1); for(int i=0;i<e;i+=2) { if(dep[b[i].v]<dep[b[i].u]) { if(po[b[i].u]==0) jg+=m; else if(po[b[i].u]==1) jg++; } if(dep[b[i].v]>dep[b[i].u]) { if(po[b[i].v]==0) jg+=m; else if(po[b[i].v]==1) jg++; } } printf("%d",jg); //while(1); return 0; }

暗之鏈鎖