Codeforces Round #686 (E,F)

本文探讨了基环树中简单路径的数量计算方法,通过分解结构找到环并利用树状结构进行路径计数。同时,文章还讨论了一个区间问题,即如何在给定数组中寻找符合条件的区间,使用单调栈和ST表等数据结构优化查找过程。

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");
    }
}
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值