[COGS 2434]暗之链锁

题目

传说中的暗之连锁被人们称为Dark。Dark是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。经过研究,你发现Dark呈现无向图的结构,图中有N个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark有N – 1条主要边,并且Dark的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark还有M条附加边。
你的任务是把Dark斩为不连通的两部分。一开始Dark的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断Dark的一条附加边。现在你想要知道,一共有多少种方案可以击败Dark。
注意,就算你第一步切断主要边之后就已经把Dark斩为两截,你也需要切断一条附加边才算击败了Dark。

INPUT

第一行包含两个整数N和M。
之后N – 1行,每行包括两个整数A和B,表示A和B之间有一条主要边。
之后M行以同样的格式给出附加边。

OUTPUT

输出一个整数表示答案。

SAMPLE

INPUT

4 1
1 2
2 3
1 4
3 4

OUTPUT

3

解题报告

第一眼看这题,还以为要用每个点的度来做= =
正经的解法:
求出每条正经的边被多少条附加边(不正经的边?(雾))所覆盖,设其为x
然后对每一条正经的边询问,只有x==0||x==1时,这条边才能被砍(正确性显然,因为如果x>1,你就算砍了它,再砍一条覆盖它的附加边,也没啥用,无法使其不连通)
当x==0时,m条边随便砍,故对答案的贡献为m
当x==1时,只有砍了覆盖它的那条附加边才有用,故对答案的贡献为1

那么问题来了,咋求这个x呢?
显然正经的边形成的是一棵树,那么我们就可以将边权下放到点权(为啥?好算啊= =),这样除了根,每个点都会有点权值,我们就可以用差分的思想来解决这个问题,dfs序跑一遍,修改时LCA-2,两端点+1(想想为什么?差分这东西,就是靠正负的抵消与修改后对区间和的影响来搞的,这样做的目的也就很明显了),那么该点权值自然就为从dfs序左端点到右端点的区间和。剩下的就十分简单了,乱搞出奇迹= =

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
int sum(0);
char ch(getchar());
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
sum=sum*10+ch-'0';
ch=getchar();
}
return sum;
}
int n,m;
struct edge{
int s,e,n;
}a[200001];
int pre[100001],tot;
inline void insert(int s,int e){
a[++tot].s=s;
a[tot].e=e;
a[tot].n=pre[s];
pre[s]=tot;
}
int fa[100001],dep[100001],size[100001],son[100001];
inline void dfs1(int u){
son[u]=0;
for(int i=pre[u];i!=-1;i=a[i].n){
int e(a[i].e);
if(e!=fa[u]){
fa[e]=u;
dep[e]=dep[u]+1;
dfs1(e);
size[u]+=size[e];
if(size[e]>size[son[u]])
son[u]=0;
}
}
}
int cnt;
int r[100001];
int top[100001],id[100001],pos[100001];
inline void dfs2(int u,int rt){
top[u]=rt;
id[u]=++cnt;
pos[cnt]=u;
if(son[u])
dfs2(son[u],rt);
for(int i=pre[u];i!=-1;i=a[i].n){
int e(a[i].e);
if(e!=fa[u]&&e!=son[u])
dfs2(e,e);
}
r[u]=cnt;
}
int sum[100001];
inline int lowbit(int x){
return x&-x;
}
inline void update(int pos,int c){
while(pos<=n){
sum[pos]+=c;
pos+=lowbit(pos);
}
}
inline int su(int pos){
int ret(0);
while(pos){
ret+=sum[pos];
pos-=lowbit(pos);
}
return ret;
}
inline int query(int l,int r){
return su(r)-su(l-1);
}
inline void swp(int &a,int &b){
a^=b;
b^=a;
a^=b;
}
inline int LCA(int x,int y){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])
swp(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline void change(int x,int y){
int lca(LCA(x,y));
update(id[lca],-2);
update(id[x],1);
update(id[y],1);
}
inline int Q(int x){
return query(id[x],r[x]);
}
typedef long long L;
L ans(0);
inline L get(int x){
if(x==0)
return m;
if(x==1)
return 1;
else
return 0;
}
inline int gg(){
freopen("yam.in","r",stdin);
freopen("yam.out","w",stdout);
memset(pre,-1,sizeof(pre));
n=read(),m=read();
for(int i=1;i<n;i++){
int x(read()),y(read());
insert(x,y);
insert(y,x);
}
dfs1(1);
dfs2(1,1);
for(int i=1;i<=m;i++){
int x(read()),y(read());
change(x,y);
}
for(int i=2;i<=n;i++){
int ret(Q(i));
ans+=get(ret);
}
printf("%d",ans);
}
int K(gg());
int main(){;}

ps:树状数组比线段树lazy快一万倍= =