[补档][COGS 2434]暗之链锁
[COGS 2434]暗之链锁
题目
传说中的暗之连锁被人们称为Dark。<!--more-->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 11 22 31 43 4OUTPUT
3
解题报告
第一眼看这题,还以为要用每个点的度来做= =
正经的解法:
求出每条正经的边被多少条附加边(不正经的边?(雾))所覆盖,设其为x
然后对每一条正经的边询问,只有x==0||x==1时,这条边才能被砍(正确性显然,因为如果x>1,你就算砍了它,再砍一条覆盖它的附加边,也没啥用,无法使其不连通)
当x==0时,m条边随便砍,故对答案的贡献为m
当x==1时,只有砍了覆盖它的那条附加边才有用,故对答案的贡献为1
那么问题来了,咋求这个x呢?
显然正经的边形成的是一棵树,那么我们就可以将边权下放到点权(为啥?好算啊= =),这样除了根,每个点都会有点权值,我们就可以用差分的思想来解决这个问题,dfs序跑一遍,修改时LCA-2,两端点+1(想想为什么?差分这东西,就是靠正负的抵消与修改后对区间和的影响来搞的,这样做的目的也就很明显了),那么该点权值自然就为从dfs序左端点到右端点的区间和。剩下的就十分简单了,乱搞出奇迹= =
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 inline int read(){ 6 int sum(0); 7 char ch(getchar()); 8 while(ch<'0'||ch>'9') 9 ch=getchar(); 10 while(ch>='0'&&ch<='9'){ 11 sum=sum*10+ch-'0'; 12 ch=getchar(); 13 } 14 return sum; 15 } 16 int n,m; 17 struct edge{ 18 int s,e,n; 19 }a[200001]; 20 int pre[100001],tot; 21 inline void insert(int s,int e){ 22 a[++tot].s=s; 23 a[tot].e=e; 24 a[tot].n=pre[s]; 25 pre[s]=tot; 26 } 27 int fa[100001],dep[100001],size[100001],son[100001]; 28 inline void dfs1(int u){ 29 son[u]=0; 30 for(int i=pre[u];i!=-1;i=a[i].n){ 31 int e(a[i].e); 32 if(e!=fa[u]){ 33 fa[e]=u; 34 dep[e]=dep[u]+1; 35 dfs1(e); 36 size[u]+=size[e]; 37 if(size[e]>size[son[u]]) 38 son[u]=0; 39 } 40 } 41 } 42 int cnt; 43 int r[100001]; 44 int top[100001],id[100001],pos[100001]; 45 inline void dfs2(int u,int rt){ 46 top[u]=rt; 47 id[u]=++cnt; 48 pos[cnt]=u; 49 if(son[u]) 50 dfs2(son[u],rt); 51 for(int i=pre[u];i!=-1;i=a[i].n){ 52 int e(a[i].e); 53 if(e!=fa[u]&&e!=son[u]) 54 dfs2(e,e); 55 } 56 r[u]=cnt; 57 } 58 int sum[100001]; 59 inline int lowbit(int x){ 60 return x&-x; 61 } 62 inline void update(int pos,int c){ 63 while(pos<=n){ 64 sum[pos]+=c; 65 pos+=lowbit(pos); 66 } 67 } 68 inline int su(int pos){ 69 int ret(0); 70 while(pos){ 71 ret+=sum[pos]; 72 pos-=lowbit(pos); 73 } 74 return ret; 75 } 76 inline int query(int l,int r){ 77 return su(r)-su(l-1); 78 } 79 inline void swp(int &a,int &b){ 80 a^=b; 81 b^=a; 82 a^=b; 83 } 84 inline int LCA(int x,int y){ 85 while(top[x]^top[y]){ 86 if(dep[top[x]]<dep[top[y]]) 87 swp(x,y); 88 x=fa[top[x]]; 89 } 90 return dep[x]<dep[y]?x:y; 91 } 92 inline void change(int x,int y){ 93 int lca(LCA(x,y)); 94 update(id[lca],-2); 95 update(id[x],1); 96 update(id[y],1); 97 } 98 inline int Q(int x){ 99 return query(id[x],r[x]); 100 } 101 typedef long long L; 102 L ans(0); 103 inline L get(int x){ 104 if(x==0) 105 return m; 106 if(x==1) 107 return 1; 108 else 109 return 0; 110 } 111 inline int gg(){ 112 freopen("yam.in","r",stdin); 113 freopen("yam.out","w",stdout); 114 memset(pre,-1,sizeof(pre)); 115 n=read(),m=read(); 116 for(int i=1;i<n;i++){ 117 int x(read()),y(read()); 118 insert(x,y); 119 insert(y,x); 120 } 121 dfs1(1); 122 dfs2(1,1); 123 for(int i=1;i<=m;i++){ 124 int x(read()),y(read()); 125 change(x,y); 126 } 127 for(int i=2;i<=n;i++){ 128 int ret(Q(i)); 129 ans+=get(ret); 130 } 131 printf("%d",ans); 132 } 133 int K(gg()); 134 int main(){;}
ps:树状数组比线段树lazy快一万倍= =