文章目录
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
http://noip.ybtoj.com.cn/contest/105/problem/3
D e s c r i p t i o n Description Description
一张无向图 G { n , m } G\{n,m\} G{n,m},给定点权和边权,现在要求降低这张图的点权,使得任意一条边权等于它连接的两个点的点权和
求最小/大的点权和
数据范围: n ≤ 5 × 1 0 5 , m ≤ 3 × 1 0 6 n\leq 5\times 10^5,m\leq 3\times 10^6 n≤5×105,m≤3×106
S o l u t i o n Solution Solution
最小点权和=点权和-最大可删除点权
最大点权和=点权和-最小可删除点权
考虑求删除点权的范围即可
假设我们选定一个点 a a a,它的权值是 x x x,现在有二条边 a − > b − > c a->b->c a−>b−>c,边权分别为 d i , d j d_i,d_j di,dj,设 b , c b,c b,c的权值为 y , z y,z y,z
则有
0
≤
d
i
−
x
≤
y
0\leq d_i-x\leq y
0≤di−x≤y移项可以得到
y
y
y的取值范围
同理,有
0
≤
d
j
−
d
i
+
x
≤
z
0\leq d_j-d_i+x\leq z
0≤dj−di+x≤z移项可以得到
z
z
z的取值范围
容易发现, a a a可以到达的点(即同一个联通块内),每个点的取值范围都是关于 x x x的一次函数 f ( x ) = k x + b f(x)=kx+b f(x)=kx+b,显然我们讨论 k k k即可得到他们的取值范围,最后取它们解集的交集即可
需要注意的是,如果构成了环,那么会得到方程
k
1
x
+
b
1
=
k
2
x
+
b
2
k_1x+b_1=k_2x+b_2
k1x+b1=k2x+b2,其中
k
1
,
b
1
k_1,b_1
k1,b1是你通过这条返祖边(构成环的那条边)得到的
k
k
k和
b
b
b,
k
2
,
b
2
k_2,b_2
k2,b2是这个点之前得到的
k
k
k和
b
b
b
讨论它解的情况即可
具体地,若
k
1
=
k
2
k_1=k_2
k1=k2,当且仅当
b
1
=
b
2
b_1=b_2
b1=b2时该方程有无限组解,否则无解
无限组解说明这个点的权值随便改,那我们可以不用管它,无解返回0即可
否则,则方程存在根
x
=
b
2
−
b
1
k
1
−
k
2
x=\frac {b_2-b_1}{k_1-k_2}
x=k1−k2b2−b1,判断这玩意儿是不是整数即可
时间复杂度: O ( n + m ) O(n+m) O(n+m)
C o d e Code Code
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 500010
using namespace std;int n,m,p[N],l[N],tot,u,v;
struct node{int next,to;LL w;}e[6000010];LL w;
inline void add(int u,int v,LL w){e[++tot]=(node){l[u],v,w};return (void)(l[u]=tot);return;}
LL sum,b[N],sumb,L,R,maxs,mins;
int k[N],sumk;
inline bool ck(int K1,LL B1,int K2,LL B2)
{
if(K1==K2) return B1==B2;
if((B2-B1)%(K1-K2)) return false;
LL w=(B2-B1)/(K1-K2);
if(w<L||w>R) return false;
return (L=R=w,true);
}
inline bool dfs(int x)
{
sumk+=k[x];sumb+=b[x];
if(k[x]==1) L=max(L,-b[x]),R=min(R,p[x]-b[x]);
else L=max(L,b[x]-p[x]),R=min(R,b[x]);
if(L>R) return 0;
for(register int i=l[x];i;i=e[i].next)
{
int y=e[i].to;
if(k[y]) {if(ck(-k[x],e[i].w-b[x],k[y],b[y])==0) return false;}
else {k[y]=-k[x];b[y]=e[i].w-b[x];if(dfs(y)==0) return false;}
}
return 1;
}
inline LL read()
{
LL d=1,f=0;char c;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
signed main()
{
freopen("diamond.in","r",stdin);
freopen("diamond.out","w",stdout);
n=read();m=read();
for(register int i=1;i<=n;i++) p[i]=read(),sum+=p[i];
for(register int i=1;i<=m;i++) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
for(register int i=1;i<=n;i++) if(k[i]==0)
{
L=sumk=sumb=0;R=p[i];k[i]=1;
if(dfs(i)==false) return puts("NIE\n")&0;
else
{
if(sumk<0) maxs+=L*sumk+sumb,mins+=R*sumk+sumb;
else maxs+=R*sumk+sumb,mins+=L*sumk+sumb;
}
}
printf("%lld %lld\n",sum-maxs,sum-mins);
}