zoukankan      html  css  js  c++  java
  • [atARC129E]Yet Another Minimization

    考虑最小割,具体建图如下——

    对变量$x_{i}$建立$m-1$个点,依次记作$V_{i,1},V_{i,2},...,V_{i,m-1}$,并定义$V_{i,0}=S,V_{i,m}=T$

    再建立$m$条边,第$j$条边为$(V_{i,j-1},V_{i,j},C_{i,j})$,割掉即表示选$A_{i,j}$作为$x_{i}$

    关于$|x_{i}-x_{j}|W_{i,j}$,实际上可以看作
    $$
    \sum_{a\in Z,x_{j}\le a,a+1\le x_{i}}W_{i,j}+\sum_{a\in Z,x_{i}\le a,a+1\le x_{j}}W_{i,j}
    $$
    将左式用$V_{i,*}$向$V_{j,*}$的边处理,根据对称性右式即会被$V_{j,*}$向$V_{i,*}$的边处理

    具体的,即要求$\forall a\in Z,V_{i,*}$向$V_{j,*}$连一条边,使得恰仅有在$A_{j,y}\le a,a+1\le A_{i,x}$时需要再割掉这条边,那么显然从$V_{i,\min_{a+1\le A_{i,x}}x-1}$向$V_{j,\max_{A_{j,y}\le a}j}$连边权为$W_{i,j}$的边即可

    将同一个点对间的边合并,显然$V_{i,x-1}$向$V_{j,y}$连的边边权和即
    $$
    \sum_{a\in Z,a+1\le A_{i,x},a\ge A_{i,x-1},A_{j,y}\le a,A_{j,y+1}\ge a+1}W_{i,j}=\max\{\min(A_{i,x},A_{j,y+1})-\max(A_{i,x-1},A_{j,y}),0\}W_{i,j}
    $$
    (特别的,不妨假设$A_{i,0}=0$且$A_{j,y}=\infty$)

    此时,对同一个$i$可能割掉多条$(V_{i,j-1},V_{i,j})$的边,这只需将$C_{i,j}$再加上一个足够大量即可避免

    (但事实上,不加反而能够通过,似乎是数据有一些问题?)

    另外,显然上式对同一个$(i,j)$至多有$o(m)$条边权非0的边,因此总边数是$o(n^{2}m)$的

    时间复杂度为$o(maxFlow(nm,n^{2}m))$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 55
     4 #define M 10
     5 #define maxV 305
     6 #define maxE 200005
     7 #define ll long long
     8 struct Edge{
     9     int nex,to;
    10     ll len;
    11 }edge[maxE];
    12 queue<int>q;
    13 int n,m,S,T,E,id[N][M],a[N][M],head[maxV],work[maxV],d[maxV];
    14 ll w;
    15 void add(int x,int y,ll z){
    16     edge[E]=Edge{head[x],y,z};
    17     head[x]=E++;
    18     if (E&1)add(y,x,0);
    19 }
    20 bool bfs(){
    21     memset(d,-1,sizeof(d));
    22     d[S]=0,q.push(S);
    23     while (!q.empty()){
    24         int k=q.front();
    25         q.pop();
    26         for(int i=head[k];i!=-1;i=edge[i].nex)
    27             if ((edge[i].len)&&(d[edge[i].to]<0)){
    28                 d[edge[i].to]=d[k]+1;
    29                 q.push(edge[i].to);
    30             }
    31     }
    32     return d[T]>=0;
    33 }
    34 ll dfs(int k,ll s){
    35     if (k==T)return s;
    36     ll ans=0;
    37     for(int &i=head[k];i!=-1;i=edge[i].nex)
    38         if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){
    39             ll p=dfs(edge[i].to,min(s,edge[i].len));
    40             edge[i].len-=p,edge[i^1].len+=p,s-=p,ans+=p;
    41             if (!s)return ans;
    42         }
    43     return ans;
    44 }
    45 ll dinic(){
    46     ll ans=0;
    47     memcpy(work,head,sizeof(head));
    48     while (bfs()){
    49         ans+=dfs(S,1e18); 
    50         memcpy(head,work,sizeof(head));
    51     }
    52     return ans;
    53 }
    54 int main(){
    55     scanf("%d%d",&n,&m),S=0,T=n*(m-1)+1;
    56     memset(head,-1,sizeof(head));
    57     for(int i=1;i<=n;i++){
    58         id[i][0]=S,id[i][m]=T;
    59         for(int j=1;j<m;j++)id[i][j]=(i-1)*(m-1)+j;
    60         for(int j=1;j<=m;j++){
    61             scanf("%d%lld",&a[i][j],&w);
    62             add(id[i][j-1],id[i][j],w);
    63         }
    64         a[i][m+1]=1e6;
    65     }
    66     for(int i=1;i<=n;i++)
    67         for(int j=i+1;j<=n;j++){
    68             scanf("%lld",&w);
    69             for(int x=1;x<=m;x++)
    70                 for(int y=1;y<=m;y++){
    71                     add(id[i][x-1],id[j][y],max(min(a[i][x],a[j][y+1])-max(a[i][x-1],a[j][y]),0)*w);
    72                     add(id[j][x-1],id[i][y],max(min(a[j][x],a[i][y+1])-max(a[j][x-1],a[i][y]),0)*w);
    73                 }
    74         }
    75     printf("%lld\n",dinic());
    76     return 0;
    77 }
    View Code
  • 相关阅读:
    一些常用的正则表达式
    ASP.net国际化页面可以选择输出语言
    SQL 2008 数据表导入到 ORACLE 10g
    转载 SQL Server 2008中增强的汇总技巧
    类似于行转列的一种需求
    第一次
    很奇怪的一个SQL 语句
    MS SQL 中 FULL JOIN 的用法
    [转载]网络编辑必知常识:什么是PV、UV和PR值 zz
    寒假学习2实验一Linux系统的安装和常用命令
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15601208.html
Copyright © 2011-2022 走看看