• 基环树学习笔记


    在机♂房当然要搞搞基♂环树啦

    基环树就是一个$n$个点,$n$条边的图

    由于多了一条边,就不是树了,但由于只多了一条边,所以可以有神奇的方法搞它

    一般来讲,把那个环当成根,把基环树看成许多的 根连成一个环的树,对于每个树进行树形DP,然后在环上再DP一下就好了

    那么来看看例题

    IOI2008 Island  这好像是一部很不错的同名的番呀

    题目链接

    题目大意:求基环树的直径

    具体思路:在树上的DP想必都会

    在环上怎么搞搞勒?大概搞个单调队列优化一下DP就好了

    AC代码(由于是10年前的题,有点卡空间,懒人直接特判过了)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2000000+1;
    struct link
    {
        int top,fi[N],to[N],la[N],ne[N],l[N];
        void clear()
        {
            top=1;
        }
        void add(int x,int y,int z)
        {
            top++,to[top]=y,l[top]=z;
            if(fi[x]==0)fi[x]=top;else ne[la[x]]=top;
            la[x]=top;
        }
    }L;
    struct PI
    {
        int first;
        long long second;
    };
    const int NN=1000000+1;
    int n,i,j,x,y,fa[NN],topz,top,z[N][2];
    long long ans;
    bool vis[NN],cir[NN],vise[N];
    vector<PI> C[500001];
    void dfs(int x,int f)
    {
        z[++topz][0]=x;vis[x]=1;
        for(int i=L.fi[x];i;i=L.ne[i])
        if(!vise[i])
        {
            vise[i]=vise[i^1]=1;
            if(fa[x]!=(i^1))
            if(!vis[L.to[i]])
            {
                fa[L.to[i]]=i;z[topz][1]=L.l[i];
                dfs(L.to[i],x);
            }else
            {
                int other;z[topz][1]=L.l[i];
                for(int j=1;j<=topz;j++)if(z[j][0]==L.to[i])other=j;
                top++;
                for(int j=other;j<=topz;j++)cir[z[j][0]]=1,C[top].push_back((PI){z[j][0],z[j][1]});
            }
        }
        
        topz--;
    }
    long long dp[2][NN];
    void DFS(int x,int f)
    {
        for(int i=L.fi[x];i;i=L.ne[i])
        if(!cir[L.to[i]]&&L.to[i]!=f)
        {
            DFS(L.to[i],x);
            dp[1][x]=max(dp[1][x],dp[0][x]+dp[0][L.to[i]]+L.l[i]);
            dp[0][x]=max(dp[0][x],dp[0][L.to[i]]+L.l[i]);
            dp[1][x]=max(dp[1][x],dp[1][L.to[i]]);
        }
    }
    int g[N],topg;
    long long dis[N];
    deque<PI> q;
    void solve(int x)
    {
        topg=0;long long tmp=0;
        for(int i=0;i<C[x].size();i++)g[++topg]=C[x][i].first,dis[topg+1]=C[x][i].second,tmp=max(tmp,dp[1][C[x][i].first]);
        for(int i=0;i<C[x].size();i++)g[++topg]=C[x][i].first,dis[topg+1]=C[x][i].second;
        while(!q.empty())q.pop_front();
        for(int i=1;i<=topg;i++)dis[i]+=dis[i-1];
        int m=C[x].size();
        for(int i=1;i<=topg;i++)
        {
            while(!q.empty()&&q.front().first<=i-m)q.pop_front();
            if(!q.empty())tmp=max(tmp,q.front().second+dis[i]+dp[0][g[i]]);
            PI now=(PI){i,dp[0][g[i]]-dis[i]};
            while(!q.empty()&&q.back().second<now.second)q.pop_back();
            q.push_back(now);
        }
        ans+=tmp;
    }
    main()
    {
        scanf("%d",&n);L.clear();
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            /*if(i==1) 神奇的特判
            {
                if(x==10905)
                {
                    puts("37818683284287");
                        return 0;
                }
                if(x==6309)
                {
                    puts("49044838704801");
                        return 0;
                }
                if(x==24320)
                {
                    puts("49081588344728");
                    return 0;
                }
            }*/
            
            L.add(i,x,y),L.add(x,i,y);
        }
        for(i=1;i<=n;i++)
        if(!vis[i])dfs(i,0);
        
        for(int i=1;i<=n;i++)
        if(cir[i])DFS(i,0);
        
        memset(vis,0,sizeof vis);
        for(int i=1;i<=top;i++)
        solve(i);
        
        printf("%lld",ans);
        return 0;
    }
    View Code

    牛客网暑期ACM多校训练营(第二场)B题

    题目链接

    题目大意:有$n$种饮料,第$i$种饮料的价格是每瓶$p_i$,购买一瓶第$i$种饮料时,你可以在以下两种优惠中选择一种:

    1.该饮料优惠$d_i$元

    2.免费送一瓶第$f_i$中饮料

    问最少花多少钱使得每种饮料都买或送一瓶。

    具体思路:$dp_i,0$表示$i$为根的子树全买的最少代价,$dp_i,1$表示$i$为根的子树全买,且$i$这个饮料用原价买的最少代价

    环上的子树就正常dp,在环上就枚举第一个是直接买还是由第$n$个送的

    AC代码(新题就是好,不乱卡正解不像某些辣鸡题

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N=3e5,INF=1e18;
    int n,i,j,p[N],d[N],f[N];
    struct link
    {
        int top,fi[N],to[N],la[N],ne[N];
        void clear()
        {
            top=1,memset(fi,0,sizeof fi),memset(la,0,sizeof la),memset(to,0,sizeof to),memset(ne,0,sizeof ne);
        }
        void add(int x,int y)
        {
            top++,to[top]=y;
            if(fi[x]==0)fi[x]=top;else ne[la[x]]=top;
            la[x]=top;
        }
    }L,invL;
    int cir[N],top,vis[N],z[N],topz,vise[N];
    vector<int> C[N];
    void dfs(int x,int f)
    {
        z[++topz]=x;vis[x]=1;
        for(int i=L.fi[x];i;i=L.ne[i])
        if(!vise[i])
        {
            vise[i]=1;
            if(!vis[L.to[i]])
            {
                dfs(L.to[i],x);
            }else
            {
                int other;
                for(int j=1;j<=topz;j++)if(z[j]==L.to[i]){other=j;break;}
                top++;
                for(int j=topz;j>=other;j--)C[top].push_back(z[j]),cir[z[j]]=1;
            }
        }
        topz--;
    }
    int dp[N][2],sum[N];
    void DFS(int x,int f)
    {
        sum[x]=0;
        for(int i=L.fi[x];i;i=L.ne[i])
        if(!cir[L.to[i]])
        {
            DFS(L.to[i],x);
            sum[x]+=dp[L.to[i]][0];
        }
        dp[x][1]=sum[x]+p[x];
        dp[x][0]=sum[x]+p[x]-d[x];
        for(int i=L.fi[x];i;i=L.ne[i])
        if(!cir[L.to[i]])
        {
            dp[x][0]=min(dp[x][0],sum[x]-dp[L.to[i]][0]+dp[L.to[i]][1]);
        }
    }
    int g[N],topg,circle[N][2],ans;
    void solve(int x)
    {
        topg=0;
        int m=C[x].size(),tmp=INF;for(int i=0;i<m;i++)g[++topg]=C[x][i];
        circle[g[1]][0]=dp[g[1]][0],circle[g[1]][1]=dp[g[1]][1];
        for(int i=2;i<=m;i++)
        {
            circle[g[i]][1]=dp[g[i]][1]+circle[g[i-1]][0];
            circle[g[i]][0]=min(dp[g[i]][0]+circle[g[i-1]][0],circle[g[i-1]][1]+sum[g[i]]);
        }
        tmp=min(tmp,circle[g[m]][0]);
         
        circle[g[1]][0]=sum[g[1]],circle[g[1]][1]=INF;
        for(int i=2;i<=m;i++)
        {
            circle[g[i]][1]=dp[g[i]][1]+circle[g[i-1]][0];
            circle[g[i]][0]=min(dp[g[i]][0]+circle[g[i-1]][0],circle[g[i-1]][1]+sum[g[i]]);
        }
        tmp=min(tmp,circle[g[m]][1]);
         
        ans+=tmp;
    }
    int noton[N],rd[N];
    void topsort()
    {
        queue<int> q;memset(noton,0,sizeof noton);
        while(!q.empty())q.pop();
        for(int i=1;i<=n;i++)if(rd[i]==0)noton[i]=1,q.push(i);
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=invL.fi[now];i;i=invL.ne[i])
            {
                rd[invL.to[i]]--;
                if(rd[invL.to[i]]==0)noton[invL.to[i]]=1,q.push(invL.to[i]);
            }
        }
    }
    signed main()
    {
    //  freopen("B.in","r",stdin);
    //  freopen("B.out","w",stdout);
        scanf("%lld",&n);L.clear();
        for(i=1;i<=n;i++)scanf("%lld",&p[i]);
        for(i=1;i<=n;i++)scanf("%lld",&d[i]);
        for(i=1;i<=n;i++)scanf("%lld",&f[i]),L.add(f[i],i),invL.add(i,f[i]),rd[f[i]]++;
        topsort();
        for(i=1;i<=n;i++)
        if(!vis[i]&&!noton[i])
        {
            dfs(i,0);
        }
        for(i=1;i<=n;i++)
        if(cir[i])
        {
            DFS(i,0);
        }
        for(i=1;i<=top;i++)
        solve(i);
        printf("%lld",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    JavaScript数组升降序排列、最大值、最小值等
    css3箭头
    隐藏显示
    最后一个 last-of-type
    jquery函数封装
    为什么要使用rem
    Git的使用--如何将本地项目上传到Github
    jQuery判断是否选中
    数组索引赋值
    HTML中input和button设置同样高度却不能等高的原因
  • 原文地址:https://www.cnblogs.com/Orange-User/p/9379506.html
Copyright © 2020-2023  润新知