zoukankan      html  css  js  c++  java
  • BZOJ2887 : 旅行

    如果小地图存在欧拉回路,那么对于大地图的每条边,都可以恰好走两次使得小地图每条边恰好经过一次,令$sum$为小地图的边权和,则此时答案为$m imes sum$。

    否则小地图存在欧拉路径,找到两个奇点$A$和$B$,那么最优解中大地图每条边一定经过$1$次或者$2$次,且仅保留经过$1$次的边后大地图里每个点度数都是$0$。

    令$d[i][j]$为小地图中$i$到$j$的最短路,若要从$i$到$j$走一次,那么最优方案是把$i,j,A,B$这$4$个点用最短路配成两对,牺牲掉这两条最短路;若要从$i$到$j$走两次,那么最优方案牺牲掉$A,B$的最短路。

    注意到$mleq n+5$,所以取大地图的一棵生成树,然后$2^{m-n+1}$暴力枚举非树边的经过次数,那么每条树边的经过次数可以从下而上递推得到。

    时间复杂度$O(p^3+n2^{n-m+1})$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=10010,M=205;
    const ll inf=1LL<<60;
    int n,m,ce,P,Q,A,B,i,j,k,x,y,z,S,at[N],f[N],e[N][2],deg[N];ll d[M][M],sum,ans,base;
    int g[N],v[N<<1],nxt[N<<1],ed,q[N],cnt;ll w1[N],w2[N];
    int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
    inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    inline void up(ll&a,ll b){a>b?(a=b):0;}
    inline ll ask1(int x,int y){
      x=at[x],y=at[y];
      return max(max(sum-d[A][x]-d[B][y],sum-d[A][y]-d[B][x]),sum-d[x][y]-d[A][B]);
    }
    inline ll ask2(int x,int y){return sum-d[A][B];}
    void dfs(int x,int y){
      f[q[++cnt]=x]=y;
      if(y)w1[x]=ask1(x,y),w2[x]=ask2(x,y);
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x);
    }
    int main(){
      scanf("%d%d%d%d",&n,&m,&P,&Q);
      for(i=1;i<=n;i++)scanf("%d",&at[i]);
      for(i=1;i<=n;i++)f[i]=i;
      for(i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        if(F(x)!=F(y))f[f[x]]=f[y],add(x,y),add(y,x);
        else{
          e[ce][0]=x;
          e[ce++][1]=y;
        }
      }
      for(i=1;i<=P;i++)for(j=1;j<=P;j++)d[i][j]=i==j?0:inf;
      while(Q--){
        scanf("%d%d%d",&x,&y,&z);
        deg[x]^=1,deg[y]^=1,sum+=z;
        d[x][y]=d[y][x]=z;
      }
      for(i=1;i<=P;i++)if(deg[i]){
        if(!A)A=i;
        else B=i;
      }
      if(!A)return printf("%lld",sum*m),0;
      for(k=1;k<=P;k++)for(i=1;i<=P;i++)for(j=1;j<=P;j++)up(d[i][j],d[i][k]+d[k][j]);
      dfs(1,0);
      for(S=0;S<1<<ce;S++){
        base=0;
        for(i=1;i<=n;i++)deg[i]=0;
        for(i=0;i<ce;i++){
          x=e[i][0],y=e[i][1];
          if(S>>i&1)deg[x]^=1,deg[y]^=1,base+=ask1(x,y);else base+=ask2(x,y);
        }
        for(i=n;i>1;i--){
          x=q[i];
          if(deg[x])deg[f[x]]^=1,base+=w1[x];else base+=w2[x];
        }
        ans=max(ans,base);
      }
      return printf("%lld",ans),0;
    }
    

      

  • 相关阅读:
    解释器模式
    享元模式
    中介者模式
    职责链模式
    命令模式
    桥接模式
    Java迭代器Iterator
    [Swift]LeetCode1247. 交换字符使得字符串相同 | Minimum Swaps to Make Strings Equal
    [Swift]LeetCode1248. 统计「优美子数组」| Count Number of Nice Subarrays
    [Swift]LeetCode1239. 串联字符串的最大长度 | Maximum Length of a Concatenated String with Unique Characters
  • 原文地址:https://www.cnblogs.com/clrs97/p/12162797.html
Copyright © 2011-2022 走看看