• BZOJ3772:精神污染


    浅谈主席树:https://www.cnblogs.com/AKMer/p/9956734.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3772

    简化题意就是求(sum)覆盖路径(i)的路径条数。

    假设路径(i)的两个端点是(u)(v),两个点的最近公共祖先是(lca)

    假如(lca!=u)并且(lca!=v):显然能覆盖这条路径的路径是从(u)的子树里出发,到(v)的子树里结束。

    假如不是上面那种情况,我们令(lca=u):能覆盖本路径的路径是从(node)子树外出发,在(v)子树内结束的路径。(node)(u)的直系儿子,是(v)的祖先。

    我们对于每一条路径,都记录两次,分别记录从(u)可以到(dfn[v]),从(v)可以到(dfn[u])。然后按第一关键字的(dfn)排序,根据第一关键字的(dfn)从小到大往主席树里塞。

    情况一里,从(u)的子树里出发的路径的第一关键字的(dfn)肯定是在(dfn[u])(dfn[u]+siz[u]-1)内的,而终点的(dfn)肯定是在区间([dfn[v],dfn[v]+siz[v]-1])里的。前者可以对应上主席树的连续一段(rt),后者则是主席树的值域下标的连续一段。

    而情况二与情况一不同的是情况二的起点(dfn)对应两个区间,分别是([1,dfn[node]-1])([dfn[node]+siz[node],n])。统计两次就行了。终点的(dfn)([dfn[v],dfn[v]+siz[v]-1])内。

    哦对了,这题卡内存,倍增求(lca)(GG),我就用树剖了。

    时间复杂度:(O(mlogn))

    空间复杂度:(O(nlogn))

    代码如下:

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int maxn=1e5+1;
    
    ll ans1,ans2;
    int n,m,tot,tim,cnt;
    int st[maxn],ed[maxn],rt[maxn<<1];
    int wson[maxn],topfa[maxn],tmp[maxn<<1];
    int now[maxn],pre[maxn<<1],son[maxn<<1];
    int fa[maxn],siz[maxn],dfn[maxn],dep[maxn];
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    	return x*f;
    }
    
    struct road {
    	int pos,v;
    	
    	road() {}
    
    	road(int _pos,int _v) {
    		pos=_pos,v=_v;
    	}
    
    	bool operator<(const road &a)const {
    		return dfn[pos]<dfn[a.pos];
    	}
    }fake[maxn<<1];
    
    void add(int a,int b) {
    	pre[++tot]=now[a];
    	now[a]=tot;son[tot]=b;
    }
    
    void dfs(int lst,int u) {
    	fa[u]=lst,dep[u]=dep[lst]+1;
    	siz[u]=1;dfn[u]=++tim;
    	for(int p=now[u];p;p=pre[p])
    		if(son[p]!=lst) {
    			int v=son[p];
    			dfs(u,v);siz[u]+=siz[v];
    			if(siz[v]>siz[wson[u]])wson[u]=v;
    		}
    }
    
    void make_top(int top,int u) {
    	topfa[u]=top;
    	if(wson[u])make_top(top,wson[u]);
    	for(int p=now[u];p;p=pre[p])
    		if(son[p]!=fa[u]&&son[p]!=wson[u])
    			make_top(son[p],son[p]);
    }
    
    int lca(int a,int b) {
    	while(topfa[a]!=topfa[b]) {
    		if(dep[topfa[a]]<dep[topfa[b]])swap(a,b);
    		a=fa[topfa[a]];
    	}
    	return dep[a]<dep[b]?a:b;
    }
    
    struct tree_node {
    	int cnt,ls,rs;
    };
    
    struct chairman_tree {
    	int tot;
    	tree_node tree[maxn*36];
    
    	void ins(int lst,int &now,int l,int r,int pos) {
    		now=++tot;tree[now]=tree[lst];tree[now].cnt++;
    		if(l==r)return; int mid=(l+r)>>1;
    		if(pos<=mid)ins(tree[lst].ls,tree[now].ls,l,mid,pos);
    		else ins(tree[lst].rs,tree[now].rs,mid+1,r,pos);
    	}
    
    	int query(int L,int R,int l,int r,int x,int y) {
    		if(x<=l&&r<=y)return tree[R].cnt-tree[L].cnt;
    		int mid=(l+r)>>1,res=0;
    		if(x<=mid)res+=query(tree[L].ls,tree[R].ls,l,mid,x,y);
    		if(y>mid)res+=query(tree[L].rs,tree[R].rs,mid+1,r,x,y);
    		return res;
    	}
    }T;
    
    int calc1(int u,int v) {
    	int a=u,node=0,res=0;
    	while(a!=v) {
    		if(fa[a]==v) {node=a;break;}
    		else if(topfa[a]==topfa[v]) {node=wson[v];break;}
    		node=topfa[a];a=fa[topfa[a]];
    	}
    	int L=dfn[node],R=dfn[node]+siz[node]-1;
    	L=lower_bound(tmp+1,tmp+cnt+1,L)-tmp-1;
    	R=upper_bound(tmp+1,tmp+cnt+1,R)-tmp-1;
    	res+=T.query(rt[0],rt[L],1,n,dfn[u],dfn[u]+siz[u]-1);
    	res+=T.query(rt[R],rt[cnt],1,n,dfn[u],dfn[u]+siz[u]-1);
    	return res-1;//减去自己这条路径
    }
    
    int calc2(int u,int v) {
    	int res=0,L=dfn[u],R=dfn[u]+siz[u]-1;
    	L=lower_bound(tmp+1,tmp+cnt+1,L)-tmp-1;
    	R=upper_bound(tmp+1,tmp+cnt+1,R)-tmp-1;
    	res=T.query(rt[L],rt[R],1,n,dfn[v],dfn[v]+siz[v]-1);
    	return res-1;//减去自己这条路径
    }
    
    ll gcd(ll a,ll b) {
    	if(!b)return a;
    	return gcd(b,a%b);
    }
    
    int main() {
    	n=read(),m=read();
    	for(int i=1;i<n;i++) {
    		int a=read(),b=read();
    		add(a,b);add(b,a);
    	}dfs(0,1);make_top(1,1);
    	for(int i=1;i<=m;i++) {
    		st[i]=read(),ed[i]=read();
    		if(dep[st[i]]<dep[ed[i]]) swap(st[i],ed[i]);//保证如果lca是其中一点肯定是ed[i];
    		fake[++cnt]=road(st[i],dfn[ed[i]]);
    		if(st[i]!=ed[i])fake[++cnt]=road(ed[i],dfn[st[i]]);
    	}sort(fake+1,fake+cnt+1);
    	for(int i=1;i<=cnt;i++)
    		tmp[i]=dfn[fake[i].pos];
    	for(int i=1;i<=cnt;i++)
    	    T.ins(rt[i-1],rt[i],1,n,fake[i].v);//建好主席树
    	for(int i=1;i<=m;i++) {
    		int tmp=lca(ed[i],st[i]);
    		if(tmp==ed[i]) ans1+=calc1(st[i],ed[i]);
    		else ans1+=calc2(st[i],ed[i]);
    	}
    	ans2=1ll*m*(m-1)/2;
    	ll g=gcd(ans1,ans2);
    	ans1/=g;ans2/=g;
    	printf("%lld/%lld
    ",ans1,ans2);
    	return 0;
    }
    
  • 相关阅读:
    [Canvas学习]变形
    [Canvas学习]样式与颜色
    [Canvas学习]绘制图形
    上海 day31--线程
    上海 day31--进程间通信IPC机制、生产者与消费者模型
    关于 序列化模块 json 的小问题和小理解!!!
    上海 day30--并发编程、进程
    上海 day29-- UDP协议通信和socketserver模块
    上海 day28--套接字socket
    易用常用的小知识点
  • 原文地址:https://www.cnblogs.com/AKMer/p/9965586.html
Copyright © 2020-2023  润新知