树上差分
树上差分
松鼠的新家
很显然的 起点+1,终点+1,然后lca和 fa[lca] 都减 1
最后统计答案时 因为终点被重复算了两次 ,所以要减1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1000002;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,root,a[N];
struct edge{
int to,nxt;
}e[N];
int hd[N],tot;
inline void add(int x,int y){
e[++tot].to=y;e[tot].nxt=hd[x];hd[x]=tot;
}
int son[N],top[N],dep[N],siz[N],fa[N];
void dfs_son(int x,int f){
siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
void dfs_chain(int x,int tp){
top[x]=tp;
if(son[x]) dfs_chain(son[x],tp);
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x]||y==son[x])continue;
dfs_chain(y,y);
}
}
int query_lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
}
return dep[x]<dep[y]?x:y;
}
int sum[N];
void get_ans(int x){
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x]) continue;
get_ans(y);
sum[x]+=sum[y];
}
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1,x,y;i<n;i++){
x=read();y=read();
add(x,y);add(y,x);
}
dfs_son(1,0);
dfs_chain(1,1);
for(int i=1,x,y;i<n;i++){
int lca=query_lca(a[i],a[i+1]);
sum[a[i]]++,sum[a[i+1]]++;
sum[lca]--,sum[fa[lca]]--;
}
get_ans(1);
for(int i=1;i<=n;i++)
printf("%d\n",sum[i]-(i!=a[1]));//除了起点的终点都重复计算了
return 0;
}
当然树链剖分也可做。就是慢的要死还冗长为了复习我还是写一下吧
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
#define ls (p<<1)
#define rs (p<<1|1)
const int N=5e6;
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,val[N],ans;
int to[N],nxt[N],hd[N],tot;
inline void add(int x,int y){
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int siz[N],dep[N],fa[N],son[N];
void dfs_son(int x,int f){
siz[x]=1;fa[x]=f;dep[x]=dep[f]+1;
for(int i=hd[x];i;i=nxt[i]){
int y=to[i];
if(y==f) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
int dfn[N],rev[N],top[N],dfn_cnt;
void dfs_chain(int x,int tp){
dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;top[x]=tp;
if(son[x]) dfs_chain(son[x],tp);
for(int i=hd[x];i;i=nxt[i]){
int y=to[i];
if(dfn[y]) continue;
dfs_chain(y,y);
}
}
struct tree{
int l,r;
int tag,sum;
}t[N];
inline void push_up(int p){
t[p].sum=(t[ls].sum+t[rs].sum);
}
void build(int l,int r,int p){
t[p].l=l;t[p].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
push_up(p);
}
void push_down(int p){
if(!t[p].tag) return;
t[ls].tag+=t[p].tag;
t[rs].tag+=t[p].tag;
t[ls].sum+=(t[ls].r-t[ls].l+1)*t[p].tag;
t[rs].sum+=(t[rs].r-t[rs].l+1)*t[p].tag;
t[p].tag=0;
}
void modify(int L,int R,int v,int p){
if(L<=t[p].l&&t[p].r<=R){
t[p].tag+=v;
t[p].sum+=(t[p].r-t[p].l+1)*v;
return;
}
push_down(p);
int mid=(t[p].l+t[p].r)>>1;
if(L<=mid) modify(L,R,v,ls);
if(R>mid) modify(L,R,v,rs);
push_up(p);
}
int query(int L,int R,int p){
if(L<=t[p].l&&t[p].r<=R) return t[p].sum;
push_down(p);
int mid=(t[p].l+t[p].r)>>1;
int res=0;
if(L<=mid) res+=query(L,R,ls);
if(R>mid) res+=query(L,R,rs);
return res;
}
void update(int x,int y,int v){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(dfn[top[x]],dfn[x],v,1);
x=fa[top[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
modify(dfn[x],dfn[y],v,1);
}
int main(){
n=read();
for(int i=1;i<=n;i++) val[i]=read();
for(int i=1,x,y;i<n;i++){
x=read();y=read();
add(x,y);add(y,x);
}
dfs_son(1,0);
dfs_chain(1,1);
build(1,n,1);
for(int i=1;i<n;i++){
int x=val[i],y=val[i+1];
update(x,y,1);
update(y,y,-1);
}
for(int i=1;i<=n;i++){
ans=query(dfn[i],dfn[i],1);
printf("%d\n",ans);
}
return 0;
}
poj 3417
题意:先给出一棵无根树,然后给出\(m\)条新边,把这\(m\)条边连上,然后每次你能毁掉两条边,规定一条是树边,一条是新边,问有多少种方案能使树断裂。
附加边即非树边,主要边是树边,非树边会和树边 形成环,如果断掉路径\((x,y)\)上的一条主要边,就需断非树边\((x,y)\)。
我们让非树边\((x,y)\)把树上\(x\)到\(y\)的路径覆盖一次,最后只需统计每条树边被覆盖几次。
如果被覆盖0次,那么切断它再切断任意一条附加边即可,
如果被覆盖1次,那么断对应的条附加边即可,
其他无论如何也切不断
那问题转化成:给定一个无向图和生成树,求每条树边被非树边覆盖了多少次。
树上差分
对每条非树边\((x,y)\)让$w[x]+1,w[y]+1,w[lca(x,y)]-2 $,然后统计以每个点为根的子树权值和 \(f[x]\) ,这就是\(x\)与其\(fa\)的连边被覆盖次数
#include <cstdio>
#include <iostream>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
const int N=400005;
int n,m;
int hd[N],nxt[N],w[N],to[N],tot;
inline void add(int x,int y,int z) {
to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
int fa[N],siz[N],son[N],dep[N];
void dfs_son(int x,int f) {
fa[x]=f;siz[x]=1;dep[x]=dep[f]+1;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==f) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
int top[N];
void dfs_chain(int x,int tp) {
top[x]=tp;
if(son[x]) dfs_chain(son[x],tp);
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs_chain(y,y);
}
}
int LCA(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
void get_ans(int x) {
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==fa[x]) continue;
get_ans(y);
w[x]+=w[y];
}
}
int ans;
int main() {
n=read();m=read();
for(int i=1,x,y;i<n;i++) {
x=read();y=read();
add(x,y,0);add(y,x,0);
}
dfs_son(1,0);
dfs_chain(1,1);
for(int i=1,x,y;i<=m;i++) {
x=read();y=read();
w[x]++;w[y]++;w[LCA(x,y)]-=2;
}
get_ans(1);
for(int i=2;i<=n;i++) {
if(w[i]==0) ans+=m;
if(w[i]==1) ans++;
}
printf("%d\n",ans);
return 0;
}
雨天的尾巴
见线段树合并