E 已知一颗基环树,问有多少种简单路径。
先找到这个环,然后环上每个点为根连接的环外的点会构成一个树,这棵树内两两构成一条路径,与每个树外节点都构成两条路径(分别走环的两端)。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=200010;
int n,m,t;
int h[N],e[N<<1],ne[N<<1],idx;
int fa[N],dfn[N],loop[N],cnt,rt;
bool st[N];
void add(int a,int b)
{
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
bool dfs(int u,int fa)
{
if(st[u])//找到环了
{
loop[++cnt]=u;
return 1;
}
st[u]=1;
for(int i=h[u];~i;i=ne[i])
{
int v=e[i];
if(v==fa) continue;
if(dfs(v,u))
{
if(loop[1]==u) return 0;//到环的起点了
loop[++cnt]=u;
return 1;
}
}
st[u]=0;//清除非环上节点的标记
return 0;
}
int dfs1(int u,int fa)
{
int sz=1;
for(int i=h[u];~i;i=ne[i])
{
int v=e[i];
if(v==fa||st[v]) continue;
sz+=dfs1(v,u);
}
return sz;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<=n;++i) st[i]=0,h[i]=-1;
idx=0;
for(int i=1;i<=n;++i)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);add(b,a);
}
cnt=rt=0;
dfs(1,-1);
ll ans=0,sum=0;
for(int i=1;i<=cnt;++i)
{
int sz=dfs1(loop[i],-1);
if(sz>1) ans+=1ll*(sz-1)*sz/2;
ans+=sum*sz*2;
sum+=sz;
}
printf("%lld\n",ans);
}
}
F
分别枚举以那个点作为最大值,假设以 点i做最大值,那么前i中不能有大于a[i]的,然后分析中间区间最少需要包括那些位置,当然是从 i 右边第一个大于a[i]的位置到 j(最后一个a[i]的位置)左边第一个大于a[i]的位置 (区间 [l,r] )。
如果区间[l,r]的最小值是a[i],区间[r+1,n]的最大值是a[i]那么就找到答案了,否则也不一定没答案,如果区间[l,r]的最小值大于a[i],那么完全可以向两边扩展进来一个a[i]使得新区间最小值是a[i].
求两端比当前值大的第一个位置用单调栈就可以,静态求区间最值用st表。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010;
int n,m,t,a[N];
vector<int> ve[N];
int lmin[N],rmin[N],lmax[N],rmax[N];
int nums[N],len;
int find(int x)
{
return lower_bound(nums+1,nums+len+1,x)-nums;
}
int f[N][20];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",a+i),nums[i]=a[i],ve[i].clear();
sort(nums+1,nums+n+1);
len=unique(nums+1,nums+n+1)-(nums+1);
for(int i=1;i<=n;++i) a[i]=find(a[i]);
stack<int> st;
for(int i=n;i>0;--i)
{
while(st.size()&&a[i]>=a[st.top()]) st.pop();
if(st.size()) rmax[i]=st.top();
else rmax[i]=n+1;
st.push(i);
}
while(st.size()) st.pop();
for(int i=1;i<=n;++i)
{
while(st.size()&&a[i]>=a[st.top()]) st.pop();
if(st.size()) lmax[i]=st.top();
else lmax[i]=0;
st.push(i);
}
for(int i=n;i>0;--i)
{
f[i][0]=a[i];
for(int j=1;j<20;++j)
f[i][j]=min(f[min(i+(1<<(j-1)),n)][j-1],f[i][j-1]);
}
for(int i=1;i<=n;++i) ve[a[i]].push_back(i);
bool flag=0;
for(int i=1;i<=n;++i)
if(ve[a[i]].size()>=3&&!lmax[i])
{
int x=ve[a[i]][ve[a[i]].size()-1];
int l=rmax[i],r=lmax[x];
if(l>r)
{
if(l==n+1&&r==0)
{
printf("YES\n%d %d %d\n",ve[a[i]][1]-1,1,n-ve[a[i]][1]);
flag=1;
break;
}
else continue;
}
if(rmax[x]!=n+1) continue;
int k=floor(log(r-l+1)/log(2));
int tmp=min(f[l][k],f[r-(1<<k)+1][k]);
if(tmp==a[i])
{
printf("YES\n%d %d %d\n",l-1,r-l+1,n-r);
flag=1;
break;
}
else if(tmp>a[i])
{
int pos=lower_bound(ve[a[i]].begin(),ve[a[i]].end(),l)-ve[a[i]].begin();
if(pos>1&&ve[a[i]][pos-1]>i)
{
k=l-ve[a[i]][pos-1]+1;
k=floor(log(k)/log(2));
tmp=min(f[ve[a[i]][pos-1]][k],f[l-(1<<k)+1][k]);
if(tmp==a[i])
{
printf("YES\n%d %d %d\n",ve[a[i]][pos-1]-1,r-ve[a[i]][pos-1]+1,n-r);
flag=1;
break;
}
}
pos=lower_bound(ve[a[i]].begin(),ve[a[i]].end(),r+1)-ve[a[i]].begin();
if(pos<ve[a[i]].size()-1)
{
k=ve[a[i]][pos]-r+1;
k=floor(log(k)/log(2));
tmp=min(f[ve[a[i]][pos]-(1<<k)+1][k],f[r][k]);
if(tmp==a[i])
{
printf("YES\n%d %d %d\n",l-1,ve[a[i]][pos]-l+1,n-ve[a[i]][pos]);
flag=1;
break;
}
}
}
}
if(!flag) puts("NO");
}
}
本文探讨了基环树中简单路径的数量计算方法,通过分解结构找到环并利用树状结构进行路径计数。同时,文章还讨论了一个区间问题,即如何在给定数组中寻找符合条件的区间,使用单调栈和ST表等数据结构优化查找过程。
1045





