YbtOJ C. 钻石守卫【图论】【数学】


题目:

传送门


题意:

每个点都有一个权值 p i p_i pi,每个边也有一个边权 w i w_i wi,我们需要调小 p i p_i pi,使得可以满足任意边的 p u + p v = w i p_u+p_v=w_i pu+pv=wi
求操作后点权最大值和最小值


分析:

可以确定的,对于每个连通块我们只需要知道其中一个点的权值就可以确定所有点的权值了
先钦定一个点的权值为 x x x,可以得到关于其他点的一元一次方程
在这里插入图片描述
一个点的权值,需要满足 ⩾ 0 \geqslant 0 0 ⩽ p i \leqslant p_i pi
当一个点有多个方程,我们就判断这些方程的解的三种情况:无穷解、唯一解、无解
无穷解显然对答案不会有影响
而唯一解我们需要做的是在得到 x x x的范围后判断是否在范围内
无解对于答案肯定是无解的
最后就按照得出的 x x x的范围的最大值和最小值代入连通块求解


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#define LL long long 
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1; s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0'; s=getchar();}
    return d*f;
}
int p[500500];
struct node{
	int to,next,w;
}e[6000050];
int ls[6000050],cnt=0;
void add(int x,int y,int w)
{
	e[cnt]=(node){y,ls[x],w};
	ls[x]=cnt++;
	return;
}
int tf[500500],T_T=0,l,r,len=0,lf[500500],lb[500500],cc=0;
void check(int k,int f,int b)
{
	if((lf[k]!=f||lb[k]!=b)&&lf[k]&lb[k])
	{
		if(b==lb[k]&&lf[k]!=f) {T_T=1;return;}
		if(b!=lb[k]&&lf[k]==f) {T_T=1;return;}
		double d=(double)(b-lb[k])/(lf[k]-f);
		if((b-lb[k])/(lf[k]-f)!=d) {T_T=1;return;}
		if(d<l||d>r) {T_T=1;return;}
		l=r=d;
	}
	lf[k]=f;lb[k]=b;
	return;
}
void dfs(int u,int f,int b)
{
	if(T_T) return;
	lf[u]=f;lb[u]=b;
	int a=ceil((double)-b/f),c=(LL)(p[u]-b)/f;
	if(f<0) swap(a,c);
	if(a>c) {T_T=1;return;}
	l=max(l,a);r=min(r,c);	
	for(int i=ls[u];~i;i=e[i].next)
	{
		int v=e[i].to;
		if(tf[v]) {check(v,f*(-1),e[i].w-b);continue;}
		tf[v]=1;
		dfs(v,f*(-1),e[i].w-b);
	}
	return;
}
LL ansl[500500],ansr[500500],s1,s2;
void ddfs(int u)
{
	s1+=ansl[u];s2+=ansr[u];
	for(int i=ls[u];~i;i=e[i].next)
	{
		int v=e[i].to;
		if(tf[v]) continue;
		tf[v]=1;
		ansr[v]=e[i].w-ansr[u];
		ansl[v]=e[i].w-ansl[u];
		ddfs(v);
	}
	return;
}
LL ss=0;
int main()
{
	freopen("diamond.in","r",stdin);
	freopen("diamond.out","w",stdout);
	memset(ls,-1,sizeof(ls));
	int n=read(),m=read();
	for(int i=1;i<=n;i++) ss+=p[i]=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read(),w=read();
		add(x,y,w);add(y,x,w);
	}
	for(int i=1;i<=n;i++)
	{
		if(tf[i]) continue;
		tf[i]=1;
		cc=0;len=1;
		l=0;r=p[i];
		dfs(i,1,0);
		if(T_T||l>r) return !printf("NIE");
		ansl[i]=l;ansr[i]=r;
	}
	LL ansi=0,ansx=0;
	memset(tf,0,sizeof(tf));
	for(int i=1;i<=n;i++)
	{
		if(tf[i]) continue;
		tf[i]=1;
		s1=0;s2=0;
		ddfs(i);
		ansi+=(min(s1,s2));
		ansx+=(max(s1,s2));
	}
	cout<<ss-ansx<<" "<<ss-ansi;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值