CF1486F Pairs of Paths
题意
给定 \(n\) 个点的树,和 \(m\) 路径,求有且仅有一个公共点的路径对数。
Solution
分类讨论一下:
\(1.\) 除了一个点的路径以外,其它的路径相互之间若要只有一个公共点,那么这个公共点必定在其中一个路径的 Lca 上。这点易证。
对于剩下的情况中,我们把路径按照 Lca 是否相同来分类,并且对于同类和不同类的两种贡献分开计算。
我们会发现,一条路经 \((x,y,z),z=\texttt{Lca}(x,y)\),另外一条路径 \((a,b)\) 与 \((x,y)\) 只有一个公共点的充要条件是:找到 \(x,y\) 对应的祖先,满足是 \(z\) 的儿子的两个点 \(x',y'\),\((a,b)\) 不经过 \(x'\) 且不经过 \(y'\)。
\(2.\) 对于不同类的两条路径,只需要对于每条路径 \((x,y)\) 查询 经过它们 Lca 的不同类路径数量,和分别 经过 \(x',y'\) 的不同类路径数量。(因为是不同类路径,所以不存在同时经过 \(x',y'\) 的路径,除了 \(\texttt{Lca}(x,y)=y\) 的情况特殊考虑一下)
\(3.\) 对于同类的,只需要容斥一下,因为两两不同的不太好算,我们用总的减去两个都相同的,以及有且仅有一个相同的数量。
大体思路就是这么简单,具体代码实现可能有一点细节需要注意。
因为这是我自己胡的 \(\operatorname O(n\log n)\) 做法,没看题解,所以可能会有更加简便的同类复杂度做法在常数上爆踩我,毕竟我是大常数选手。
说不定不是常数的问题,因为我预处理的复杂度是 \(\operatorname O(n\log n)\) 的,如果能够有 \(\operatorname O(n)\) 处理出 Lca 以及 \(x',y'\) 的东西,那这道题的复杂度就能够达到 \(\operatorname O(n)\) 了。
不知道 CF 上 DP 的标签是怎么回事,说不定有 DP 的做法。
Code
#include <vector>
#include <stdio.h>
#include <algorithm>
#define LL long long
#pragma GCC optimize(3)
using namespace std;
const int Rea=1e5+3;
struct Rin
{
char c;
inline char gc()
{
static char rea[Rea];
static char *head,*tail;
return head==tail&&(tail=(head=rea)+fread(rea,1,Rea,stdin),head==tail)?EOF:*head++;
}
inline Rin&operator >>(int &x)
{
x=0;
bool tag=false;
for(c=gc();c>'9'||c<'0';c=gc())if(c=='-'){c=gc();tag=true;break;}
for(;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c^'0');
if(tag)x=-x;
return *this;
}
}rin;
inline void jh(int &x,int &y){if(x^y)x^=y^=x^=y;return;}
const int N=3e5+3;
int n,m;
namespace St
{
vector<int>nxt[N];
int f[20][N];
int dep[N];
inline void dfs(int x,int fa){dep[x]=dep[fa]+1;f[0][x]=fa;for(vector<int>::iterator i=nxt[x].begin();i!=nxt[x].end();i++)if((*i)!=fa)dfs(*i,x);}
inline int Lca(int x,int y)
{
if(dep[x]<dep[y])jh(x,y);
for(int i=dep[x]-dep[y],j=0;i;i>>=1,j++)if(i&1)x=f[j][x];
for(int j=0;j>=0;){if(f[j][x]!=f[j][y])x=f[j][x],y=f[j][y],j++;else j--;}
if(x!=y)x=f[0][x];
return x;
}
inline int find(int x,int y){for(int i=dep[x]-dep[y]-1,j=0;i>0;i>>=1,j++)if(i&1)x=f[j][x];return x;}
}
using namespace St;
vector<int>a[N];
vector<pair<int,int> >b[N];
int les[N];
int cutt[N];
int ruir[N];
int ruier[N];
LL ans;
inline void init_ans(int x,int fa)
{
int cuts=a[x].size()+b[x].size();
for(vector<int>::iterator i=nxt[x].begin();i!=nxt[x].end();i++)if((*i)!=fa)init_ans(*i,x),ruir[x]+=ruir[*i];
ans+=1LL*ruir[x]*ruier[x];ans+=1LL*ruier[x]*(ruier[x]-1)>>1;
for(vector<int>::iterator i=a[x].begin();i!=a[x].end();i++)ruir[*i]--;
for(vector<pair<int,int> >::iterator i=b[x].begin();i!=b[x].end();i++)ruir[i->first]--,ruir[i->second]--;
for(vector<int>::iterator i=a[x].begin();i!=a[x].end();i++)ans+=(ruir[x]-cuts-ruir[*i]);
for(vector<pair<int,int> >::iterator i=b[x].begin();i!=b[x].end();i++)ans+=(ruir[x]-cuts-ruir[i->first]-ruir[i->second]);
ans+=1LL*cuts*(cuts-1)>>1;
sort(b[x].begin(),b[x].end());
for(vector<int>::iterator j=a[x].begin();j!=a[x].end();j++)ans-=cutt[*j],cutt[*j]++;
for(vector<pair<int,int> >::iterator j=b[x].begin(),k;j!=b[x].end();j=k)
{
int nows=1;
for(k=j+1;k!=b[x].end()&&(*j)==(*k);k++)nows++;
ans-=1LL*nows*(nows-1)>>1;
ans-=1LL*(cutt[j->first]+cutt[j->second])*nows;
cutt[j->first]+=nows;cutt[j->second]+=nows;
}
for(vector<int>::iterator j=a[x].begin();j!=a[x].end();j++)cutt[*j]--;
for(vector<pair<int,int> >::iterator j=b[x].begin();j!=b[x].end();j++)cutt[j->first]--,cutt[j->second]--;
ruir[x]-=les[x];
return;
}
int main()
{
rin>>n;for(int i=1;i<n;i++){int x,y;rin>>x>>y;nxt[x].push_back(y);nxt[y].push_back(x);}
dfs(1,0);for(int i=1;(1<<i)<=n;i++)for(int j=1;j<=n;j++)f[i][j]=f[i-1][f[i-1][j]];
rin>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
rin>>x>>y;
if(x==y){ruier[x]++;continue;}
if(dep[x]<dep[y])jh(x,y);
z=Lca(x,y);
int x_=find(x,z),y_=find(y,z);
if(y==z)a[z].push_back(x_);
else b[z].push_back(make_pair(min(x_,y_),max(x_,y_)));
ruir[x]++;ruir[y]++;ruir[z]--;les[z]++;
}
init_ans(1,0);
printf("%lld\n",ans);
return 0;
}