[Lydsy1806月赛] 路径统计
xjb想的做法竟然不小心把std艹爆了qwq,我也很无奈啊....
那接下来就说一下我的神奇做法qwq
如果是经常读我博客的童鞋会发现其实我以前就想要做这个题啦,只不过当时读错题啦。。。以为[L,R]在树上只要形成联通块就可以了,于是就自己出了一个题
那个题的做法是直接扫描线,因为[L,R]合法当且仅当 R-L = sum[L] ,其中sum[L] 表示以L 为左端点,目前扫描线为右端点的区间中在树上相邻的点对数,而扫描线右端点右移一位造成的影响只会是一些前缀的sum[]区间加,用线段树动态维护一下就好啦。(维护 区间加,最大值和其数量)
于是那个题就这么做完了。。。。
但是这个题还得要求 在树上形成的联通块是一个链。。。。。
不过仔细想想,链无非就比联通块多一个限制:所有点的度数<=2
所以我们就可以对于每个扫描线右端点R,去动态维护一个L,使得区间 [L,R] 在树上是若干链(当然也可以是一条啦),但是 [L-1,R]的点构成的子图中就有至少一个点的度数>2了。
不难想到 L 关于 R 是具有单调性的,R增大L不会变小,并且 [L,R] 满足没有 >2的度数的点的话,[L+1,R]也满足;反之,[L,R]不满足的话那么 [L-1,R] 也不满足。。。。
所以就可以用度数的关系轻松的O(N)维护这个玩意,复杂度还是在扫描线线段树那里。
于是这个题也这么做完了23333
#include<bits/stdc++.h> #define ll long long using namespace std; #define pb push_back #define lc (o<<1) #define mid (l+r>>1) #define rc ((o<<1)|1) const int N=250005; inline int read(){ int x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x; } vector<int> g[N],h[N]; int mx[N*4],tag[N*4],n,m,le,ri; int dg[N],num,L,sum[N*4],M,cnt,R; ll ans=0; inline void mt(int o){ mx[o]=max(mx[lc],mx[rc]); sum[o]=(mx[lc]==mx[o]?sum[lc]:0)+(mx[rc]==mx[o]?sum[rc]:0); } inline void work(int o,int der){ tag[o]+=der,mx[o]+=der;} inline void pd(int o){ if(tag[o]){ work(lc,tag[o]),work(rc,tag[o]); tag[o]=0; } } void build(int o,int l,int r){ sum[o]=1,mx[o]=r; if(l==r) return; build(lc,l,mid),build(rc,mid+1,r); } void update(int o,int l,int r){ if(l>=le&&r<=ri){ work(o,1); return;} pd(o); if(le<=mid) update(lc,l,mid); if(ri>mid) update(rc,mid+1,r); mt(o); } void query(int o,int l,int r){ if(l>=le&&r<=ri){ if(mx[o]>M) M=mx[o],cnt=sum[o]; else if(mx[o]==M) cnt+=sum[o]; return; } pd(o); if(le<=mid) query(lc,l,mid); if(ri>mid) query(rc,mid+1,r); } inline void ADD(int x){ le=1; for(int j=g[x].size()-1,i;j>=0;j--){ i=g[x][j],ri=i,update(1,1,n); if(i>=L) num+=((++dg[x])==3)+((++dg[i])==3); } } inline void Del(int x){ for(int j=h[x].size()-1,i;j>=0;j--){ i=h[x][j]; if(i<=R) num-=((--dg[x])==2)+((--dg[i])==2); } } inline void solve(){ build(1,1,n),L=1; for(int i=1;i<=n;i++){ ADD(i),R=i; for(;num;L++) Del(L); le=L,ri=i,M=0,query(1,1,n); if(M==i) ans+=(ll)cnt; } } int main(){ n=read(); for(int i=1,uu,vv;i<n;i++){ uu=read(),vv=read(); if(uu<vv) g[vv].pb(uu),h[uu].pb(vv); else g[uu].pb(vv),h[vv].pb(uu); } solve(),printf("%lld\n",ans); return 0; }
我爱学习,学习使我快乐