1. 程式人生 > 其它 >題解 P3275 【[SCOI2011]糖果】

題解 P3275 【[SCOI2011]糖果】

P3275 [SCOI2011]糖果

題目大意:

求滿足 \(k\) 個不等關係的最小正整數解的和。

solution:

這種不等式關係,考慮用差分約束求解。
我們逐一擊破每個操作:
\(c_i\) 為編號為 \(i\) 的小朋友的糖果數。

  1. \(c_a=c_b \longrightarrow c_a \geq c_b+0\)\(c_a \geq c_b+0\)
  2. $c_a \leq c_b \longrightarrow c_b \geq c_a+1 $。
  3. \(c_a \geq c_b \longrightarrow c_a \geq c_b+0\)
  4. \(c_a > c_b \longrightarrow c_a \geq c_b+1\)
  5. \(c_a \leq c_b \longrightarrow c_b \geq c_a+0\)

按照差分約束連邊後跑最長路即可。

細節處理:

  • 注意判斷無解情況,輸出 \(-1\)
  • 結果用 \(\text{long long }\) 儲存。
  • 源點倒序連線,玄學優化。
  • 操作 \(2\)\(4\) 特判自己不能比自己多/少。
程式碼
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=2e5+5;
int n,m;
int hd[N],nt[N<<1],to[N<<1],ww[N<<1],cnt;
inline void tian(int x,int y,int z){
	to[++cnt]=y,ww[cnt]=z,nt[cnt]=hd[x],hd[x]=cnt;
}
int dis[N],num[N];bool vis[N];
inline bool spfa(){
	queue<int> q;
	dis[0]=0,vis[0]=1;
	q.push(0);
	while(q.size()){
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=hd[x];i;i=nt[i]){
			int y=to[i],z=ww[i];
			if(dis[y]<dis[x]+z){
				dis[y]=dis[x]+z;
				if(!vis[y]){
					vis[y]=1,num[y]++;
					if(num[y]==n+1) return 0;
					q.push(y);
				}
			}
		}
	}
	return 1;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=n;i>=1;i--) tian(0,i,1);//玄學優化
	for(int i=1,op,x,y;i<=m;i++){
		scanf("%d%d%d",&op,&x,&y);
		if(op==1) tian(x,y,0),tian(y,x,0);
		else if(op==2) tian(x,y,1);
		else if(op==3) tian(y,x,0);
		else if(op==4) tian(y,x,1);
		else if(op==5) tian(x,y,0);
		if(op%2==0&&x==y) return puts("-1"),0;//特判
	}
	if(!spfa()) return puts("-1"),0;//無解
	long long ans=0;
	for(int i=1;i<=n;i++) ans+=dis[i];//統計答案
	printf("%lld",ans);
	return 0;
}

End