码迷,mamicode.com
首页 > 其他好文 > 详细

[Luogu P5680][GZOI2017]共享单车

时间:2020-01-09 22:42:29      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:algo   注意   fread   set   include   不难   continue   etc   sdi   

题目链接

之前没看懂题意就把这题扔了,现在一看是读错题意了

简化版题意

给出一颗树(这个图的最短路径生成树),每个点初始颜色为\(0\)

两种操作:

  1. 将部分点颜色取反

  2. 给出一些点,建出虚树(边权为两点树上距离),求最小割边代价使得虚树上没有颜色为\(1\)的点与根联通

那这就很模板了:

\(f[x]\)表示\(x\)子树的答案,\(c[x]\)表示\(x\)的颜色,则有转移方程:(当\(f[K]=0\)时输出\(-1\)
\[ f[x]+=Val(x,y)\quad (c[y]=1)\f[x]+=\min (Val(x,y),f[y])\quad (c[y]=0) \]

(注意以上方法没有考虑边权全为\(0\)但是答案不为\(-1\)的情况,不过好像没有这种数据,反正也不难特判就没管了)

时间复杂度 \(O((N+M)\log N+Qnum(\log num+\log N))\)

(数据为什么这么小)

代码:

#include <queue>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>

inline char Getchar()
{
    static char In[1<<22],*p1=In,*p2=In;
    return p1==p2&&(p2=(p1=In)+fread(In,1,1<<22,stdin),p1==p2)?EOF:*p1++;
}

inline int Getint()
{
    int x=0,c;
    while(!isdigit(c=Getchar()));
    for(;isdigit(c);c=Getchar())x=x*10+(c^48);
    return x;
}

const int N=50005,Inf=0x7f7f7f7f;
int n,M,K,Q,Dis[N],Pre[N];
int Dfn[N],Tim,Dep[N],f[N][16];
int c[N],a[N],s[N],Top;
std::vector<int> G[N],Gv[N],T[N],V[N];
//Graph|Graph Value|Tree|Virtual Tree

void Dijkstra()
{
    struct Rec{int x,d;inline bool operator<(const Rec& o)const{return d>o.d;}};
    std::priority_queue<Rec> q;
    memset(Dis,0x7f,sizeof Dis);
    q.push((Rec){K,Dis[K]=0});
    while(!q.empty())
    {
        int x=q.top().x,d=q.top().d;
        if(q.pop(),d!=Dis[x])continue;
        for(int i=0,y,v;i<(int)G[x].size();++i)
            if(Dis[y=G[x][i]]>Dis[x]+(v=Gv[x][i])||(Dis[y]==Dis[x]+v&&x<Pre[y]))
                q.push((Rec){y,Dis[y]=Dis[x]+v}),Pre[y]=x;
    }
    for(int i=1;i<=n;++i)if(i!=K)T[Pre[i]].push_back(i);
}

void DFS(int x,int Fa)
{
    Dfn[x]=++Tim,Dep[x]=Dep[f[x][0]=Fa]+1;
    for(int i=1;i<=15;++i)f[x][i]=f[f[x][i-1]][i-1];
    for(auto y:T[x])if(y!=Fa)DFS(y,x);
}

int Lca(int x,int y)
{
    if(Dep[x]<Dep[y])std::swap(x,y);
    for(int i=15;i>=0;--i)if(Dep[f[x][i]]>=Dep[y])x=f[x][i];
    if(x==y)return y;
    for(int i=15;i>=0;--i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[y][0];
}

int DP(int x)
{
    int Res=0;
    for(auto y:V[x])
    {
        int v=DP(y);
        if(c[y])Res+=Dis[y]-Dis[x];
        else Res+=std::min(v,Dis[y]-Dis[x]);
    }
    V[x].clear();
    return Res;
}

int main()
{
    //freopen("in.txt","r",stdin);
    n=Getint(),M=Getint(),K=Getint(),Q=Getint();
    for(int i=1,x,y,z;i<=M;++i)
    {
        x=Getint(),y=Getint(),z=Getint();
        G[x].push_back(y),Gv[x].push_back(z);
        G[y].push_back(x),Gv[y].push_back(z);
    }
    Dijkstra(),DFS(K,0);
    for(int o,k;Q--;)
        if(o=Getint(),k=Getint(),!o)while(k--)c[Getint()]^=1;
        else
        {
            for(int i=1;i<=k;++i)a[i]=Getint();
            s[Top=1]=K,std::sort(a+1,a+k+1,[](int x,int y){return Dfn[x]<Dfn[y];});
            for(int i=1;i<=k;++i)
            {
                int Ls=Lca(s[Top],a[i]);
                while(Top>1&&Dfn[s[Top-1]]>=Dfn[Ls])V[s[Top-1]].push_back(s[Top]),--Top;
                if(s[Top]!=Ls)V[Ls].push_back(s[Top]),s[Top]=Ls;
                s[++Top]=a[i];
            }
            while(Top>1)V[s[Top-1]].push_back(s[Top]),--Top;
            int Ans=DP(K);
            printf("%d\n",Ans?Ans:-1);
        }
    return 0;
}

[Luogu P5680][GZOI2017]共享单车

标签:algo   注意   fread   set   include   不难   continue   etc   sdi   

原文地址:https://www.cnblogs.com/LanrTabe/p/12173581.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!