【清华冬令营2018模拟】变量

10 篇文章 0 订阅
4 篇文章 0 订阅

【题目描述】

有n个变量w[1]~w[n],每个变量可以取W或-W。
有p个式子,形如Hi=ai|w[xi]-w[yi]|+bi|w[yi]-w[zi]|+ci|w[zi]-w[xi]|
+di(w[xi]-w[yi])+ei(w[yi]-w[zi])+fi(w[zi]-w[xi])。
有q个条件,形如w[x]<=w[y]或w[x]=w[y]或w[x]

【输入数据】

第一行一个整数t表示数据组数。
每组数据第一行四个整数n,W,p,q表示节点数。
接下来p行每行九个整数xi,yi,zi,ai,bi,ci,di,ei,fi。
接下来q行每行三个整数x,y,r。
r=0表示w[x]<=w[y];r=1表示w[x]=w[y];r=2表示w[x]

【输出数据】

每组数据输出一行一个整数表示sigma(wi)+sigma(Hi)的最小值。

【样例输入】

1
3 1 1 1
1 2 3 1 1 1 1 1 1
1 2 2

【样例输出】

3

【数据范围】

对于30%的数据,n<=15,p,q<=20。
对于100%的数据,t<=10,n<=500,p,q<=1000。

Solution

好久没打二元关系了,刚好用这题想起来了二元关系这个东西
注意到与d,e,f有关的可以直接乘进去,所以可以直接当成本身的全职,而前面a,b,c是有绝对值的,处理起来麻烦一点
由于w只有正负两种取值,可以发现,只有当取值互为相反数时才有用
所以不管它x,y,z,全部分成xy,xz,yz分别做
为了方便,只讨论xy,后两个一样
现在考虑x,y
首先源点向x连权值为(d-f)w+w,x向汇点连-(d-f)w-w
表示割掉源点边表示选正,割掉汇点边表示选负
对于y类似的
源点向x连权值为(e-d)w+w,x向汇点连-(e-d)w-w
然后x向y连双向边,边权为2wa,即两个分别归s集和y集,即选的相反,则需要割掉这条边

接下来考虑那几个限制
源点为S,汇点为T
限制x

Code

当时脑抽还分了点,实际不用

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 101000
#define INF 2147483647000000
#define DA 100000000000
#define M 1010
#define ll long long
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
int S,T,n,m,p,q,last[M],next[N],to[N],tot=1,bz[M],d[M];
ll date[N],v[M],v0[M],v1[M],va[M][M],ans=0;
void putin(int x,int y,ll z)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;date[tot]=z;
    next[++tot]=last[y];last[y]=tot;to[tot]=x;date[tot]=0;
//  printf("%d %d %d\n",x,y,z);
}
bool bfs()
{
    cl(bz);
    int he=0,ta=1;d[1]=S;bz[S]=1;
    while(he<ta)
    {
        int x=d[++he];
        for(int i=last[x];i;i=next[i])
        if(date[i]&&!bz[to[i]]) bz[to[i]]=bz[x]+1,d[++ta]=to[i];
    }
    return bz[T];
}
ll dfs(int x,ll t)
{
    if(x==T) return t;
    ll jy=0;
    for(int i=last[x];i;i=next[i])
    if(date[i])
    {
        int y=to[i];if(bz[y]!=bz[x]+1) continue;
        ll qq=dfs(y,min(t,date[i]));
        date[i]-=qq;date[i^1]+=qq;jy+=qq;t-=qq;
        if(t==0) return jy;
    }
    if(jy==0) bz[x]=-1;
    return jy;
}
int main()
{
    freopen("variable.in","r",stdin);
    freopen("variable.out","w",stdout);
    int ac;scanf("%d",&ac);
    while(ac--)
    {
        cl(last);tot=1;cl(va);cl(v);cl(v0);cl(v1);ans=0;
        scanf("%d%d%d%d",&n,&m,&p,&q);
        S=n+n+1;T=S+1;
        fo(i,1,p)
        {
            ll x,y,z,a,b,c,d,e,f;
            scanf("%lld%lld%lld%lld%lld%lld%lld%lld%lld",&x,&y,&z,&a,&b,&c,&d,&e,&f);
            v[x]+=d-f;v[y]+=e-d;v[z]+=f-e;
            va[x][y]+=a*m*2;va[y][x]+=a*m*2;
            va[y][z]+=b*m*2;va[z][y]+=b*m*2;
            va[x][z]+=c*m*2;va[z][x]+=c*m*2;
        }
        fo(i,1,q)
        {
            int x,y,z;scanf("%d%d%d",&x,&y,&z);
            if(z==0)
            {
                va[y][x]+=INF;
            }
            if(z==1)
            {
                va[x][y]+=INF;
                va[y][x]+=INF;
            }
            if(z==2)
            {
                v0[x]+=INF;
                v1[y]+=INF;
            }
        }
        fo(i,1,n)
        {
            putin(S,i,m+m*v[i]+v0[i]+DA),putin(i+n,T,-m-m*v[i]+v1[i]+DA),putin(i,i+n,INF);
            fo(j,1,n) if(va[i][j]!=0) putin(i,j+n,va[i][j]);
        }
        while(bfs()) ans+=dfs(S,INF);
        printf("%lld\n",ans-DA*n);
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值