把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P5327 [ZJOI2019]语言

题面传送门
感觉自己可能哪里写烦了,写了个\(O(n\log^2n)\)的大暴力。
考虑对于一个点\(u\)求有多少个点能和他进行贸易。
容易发现在一次语言的传播过程中,如果\(u\)\(x->y\)的路径上,那么\(u\)能和\(x->y\)路径上所有点进行交易。
所以可以将这个路径上所有点的\(x->y\)都打上标记,那么最终要求的就是每个点上有标记的点的数量。
套个树剖写个线段树合并就可以做到\(O(n\log^2n)\)空间了。
但是这个空间复杂度很逊如果不写个标记永久化啥的肯定点名被卡。
考虑\(u\)点能走到的点肯定是一个联通块。所以答案就是过\(u\)点的链两端点的最小斯坦纳树的大小。
然后线段树合并时随便搞搞就好了,写个\(O(1)\)的LCA可以做到\(O(n\log n)\)时空复杂度。
但是只有树剖的代码
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (100000+5)
#define M (N*200)+5
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;vector<int> S[N+5];struct Ques{int l,r,w;}Tm;vector<Ques> Q[N+5];ll ToT,Ans;
int n,m,k,x,y,Tp[N+5],d[N+5],fa[N+5],Id[N+5],Is,Si[N+5],Sn[N+5],Ro[N+5];
I void dfs1(int x,int La){d[x]=d[fa[x]=La]+1;Si[x]=1;for(RI i:S[x]) i^La&&(dfs1(i,x),Si[x]+=Si[i],Si[Sn[x]]<Si[i]&&(Sn[x]=i));}
I void dfs2(int x,int La){Tp[x]=La;Id[x]=++Is;if(!Sn[x]) return;dfs2(Sn[x],La);for(RI i:S[x]) i^fa[x]&&i^Sn[x]&&(dfs2(i,i),0);}
namespace Tree{
	int cnt,L[M],R[M],W[M],S[M],Fl[M];I void PF(int now,int w){Fl[now]+=w;W[now]+=w;}
	I void Up(int now,int l,int r){int m=l+r>>1,L1=W[L[now]],L2=S[L[now]],R1=W[R[now]],R2=S[R[now]];!L[now]&&(L1=0,L2=m-l+1);!R[now]&&(R1=0,R2=r-m);L1^R1?(L1<R1?(W[now]=L1,S[now]=L2):(W[now]=R1,S[now]=R2)):(W[now]=L1,S[now]=L2+R2);W[now]+=Fl[now];assert(S[now]<=r-l+1);}
	I void Ins(int x,int y,int w,int &now,int l=1,int r=n){!now&&(S[now=++cnt]=r-l+1);if(x<=l&&r<=y) return PF(now,w);int m=l+r>>1;x<=m&&(Ins(x,y,w,L[now],l,m),0);y>m&&(Ins(x,y,w,R[now],m+1,r),0);Up(now,l,r);}
	I int ME(int x,int y,int l=1,int r=n){if(!x||!y) return x|y;if(l==r) {S[x]=1;W[x]+=W[y];Fl[x]+=Fl[y];return x;}int m=l+r>>1;Fl[x]+=Fl[y];L[x]=ME(L[x],L[y],l,m);R[x]=ME(R[x],R[y],m+1,r);Up(x,l,r);return x;}
}
I int LCA(int x,int y){while(Tp[x]^Tp[y]) d[Tp[x]]<d[Tp[y]]&&(swap(x,y),0),x=fa[Tp[x]];return d[x]<d[y]?x:y;}
I void Ins(int x,int y){int Ls=LCA(x,y),X=x,Y=y;while(Tp[x]^Tp[y]) d[Tp[x]]<d[Tp[y]]&&(swap(x,y),0),Tm=(Ques){Id[Tp[x]],Id[x],1},Q[X].PB(Tm),Q[Y].PB(Tm),Tm.w=-1,Q[Ls].PB(Tm),Q[fa[Ls]].PB(Tm),x=fa[Tp[x]];d[x]>d[y]&&(swap(x,y),0);Tm=(Ques){Id[x],Id[y],1};Q[X].PB(Tm);Q[Y].PB(Tm);Tm.w=-1;Q[Ls].PB(Tm);Q[fa[Ls]].PB(Tm);}
I void Solve(int x,int La){for(RI i:S[x]) i^fa[x]&&(Solve(i,x),Ro[x]=Tree::ME(Ro[x],Ro[i]));for(Ques d:Q[x]) Tree::Ins(d.l,d.r,d.w,Ro[x]);Ans=n-(Tree::W[Ro[x]]?0:(Ro[x]?Tree::S[Ro[x]]:n));Ans&&(Ans--);ToT+=Ans;}
int main(){
	freopen("1.in","r",stdin);
	RI i;scanf("%d%d",&n,&m);for(i=1;i<n;i++)scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x);dfs1(1,0);dfs2(1,1);while(m--) scanf("%d%d",&x,&y),Ins(x,y);Solve(1,0);printf("%lld\n",ToT/2);
} 
posted @ 2022-03-03 15:58  275307894a  阅读(26)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end