[POI2012] BEZ-Minimalist Security

一张n个点m条边的无向图,有点权有边权都是非负,且每条边的权值小于等于两个顶点的权值和,现在要将每个点减一个非负整数使得每条边权等于两个顶点的点权和,问最大修改代价和最小修改代价

 

思路神的一匹,完全想不出来,对着题解想了半天才有点理解

首先有一个小结论:对于一个联通块,如果一个顶点的值确定了,其余顶点的值都能确定。这是显然的,因为直接用一条边的边权减去已知点权就是另一个点的权值。如果我们设一个点的权值为x,与之相连的边权为w,另一点点权即为w-x

这样的话其实整个联通块内所有的点权都可以表示成y=k*x+b(k∈(-1,1))的形式,我们对于解一下关于y的不等式即可

特别注意的是,如果图中存在奇环,那么某个点会存在两种系数不同的表示,这时我们直接解这个方程就可以求出x的唯一解

这时我们还得保证x解出来为整数,这也是做这个题目要注意的的一点

 

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #include<cstdlib>
 6 #define N 300010
 7 #define M 5000010
 8 #define ll long long
 9 using namespace std;
10 queue<int>qx,qy;
11 
12 int n,m,num;
13 int head[N],val[N],q[N];
14 bool vis[N][2];
15 ll ans1,ans2,v[N][2];
16 
17 int read()
18 {
19     char ch=getchar(); int f=1,x=0;
20     while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch=getchar();}
21     while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
22     return x*f;
23 }
24 
25 struct point{
26     int next,to,dis;
27 }e[M<<1];
28 
29 void add(int from,int to,int dis)
30 {
31     e[++num].next=head[from];
32     e[num].to=to;
33     e[num].dis=dis;
34     head[from]=num;
35 }
36 
37 void bfs(int x)
38 {
39     vis[x][0]=1;
40     qx.push(x); qy.push(0);
41     int tot=0; q[++tot]=x;
42     while(!qx.empty())
43     {
44         int a=qx.front(),b=qy.front();
45         qx.pop(); qy.pop(); 
46         for(int i=head[a];i;i=e[i].next)
47         {
48             int to=e[i].to;
49             if(!vis[to][0]&&!vis[to][1]) q[++tot]=to;
50             if(vis[to][b^1])
51             {
52                 if(v[to][b^1]!=e[i].dis-v[a][b]) {printf("NIE"); exit(0);}
53             }
54             else
55             {
56                 vis[to][b^1]=1,v[to][b^1]=e[i].dis-v[a][b];
57                 qx.push(to); qy.push(b^1);
58             }
59         }
60     }
61     ll L=0,R=val[x],sum1=0,sum2=0;
62     for(int i=1;i<=tot;i++)
63     {
64         int a=q[i];
65         if(vis[a][0]) L=max(L,-v[a][0]),R=min(R,val[a]-v[a][0]);
66         if(vis[a][1]) L=max(L,v[a][1]-val[a]),R=min(R,v[a][1]);
67         if(vis[a][0]&&vis[a][1])
68         {
69             if((v[a][1]-v[a][0])&1) {printf("NIE"); exit(0);}
70             L=max(L,(v[a][1]-v[a][0])>>1);
71             R=min(R,(v[a][1]-v[a][0])>>1);
72         }
73     }
74     if(L>R) {printf("NIE"); exit(0);}
75     for(int i=1;i<=tot;i++)
76     {
77         int a=q[i];
78         if(vis[a][0]) sum1+=val[a]-L-v[a][0],sum2+=val[a]-R-v[a][0];
79         else sum1+=val[a]+L-v[a][1],sum2+=val[a]+R-v[a][1];
80     }
81     if(sum1>sum2) swap(sum1,sum2);
82     ans1+=sum1,ans2+=sum2;
83 }
84 
85 int main()
86 {
87     n=read(); m=read();
88     for(int i=1;i<=n;i++) val[i]=read();
89     for(int i=1;i<=m;i++)
90     {
91         int x=read(),y=read(),z=read();
92         add(x,y,z); add(y,x,z);
93     }
94     for(int i=1;i<=n;i++)
95         if(!vis[i][0]&&!vis[i][1])
96             bfs(i);
97     printf("%lld %lld",ans1,ans2);
98     return 0;
99 }

 

转载于:https://www.cnblogs.com/Slrslr/p/9540146.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值