P3275 [SCOI2011] 糖果(差分约束)
P3275 [SCOI2011] 糖果
题目描述
幼儿园里有 \(N\) 个小朋友,\(\text{lxhgww}\) 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,\(\text{lxhgww}\) 需要满足小朋友们的 \(K\) 个要求。幼儿园的糖果总是有限的,\(\text{lxhgww}\) 想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入格式
输入的第一行是两个整数 \(N\),\(K\)。接下来 \(K\) 行,表示这些点需要满足的关系,每行 \(3\) 个数字,\(X\),\(A\),\(B\)。
- 如果 \(X=1\), 表示第 \(A\) 个小朋友分到的糖果必须和第 \(B\) 个小朋友分到的糖果一样多;
- 如果 \(X=2\), 表示第 \(A\) 个小朋友分到的糖果必须少于第 \(B\) 个小朋友分到的糖果;
- 如果 \(X=3\), 表示第 \(A\) 个小朋友分到的糖果必须不少于第 \(B\) 个小朋友分到的糖果;
- 如果 \(X=4\), 表示第 \(A\) 个小朋友分到的糖果必须多于第 \(B\) 个小朋友分到的糖果;
- 如果 \(X=5\), 表示第 \(A\) 个小朋友分到的糖果必须不多于第 \(B\) 个小朋友分到的糖果;
输出格式
输出一行,表示 \(\text{lxhgww}\) 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 \(-1\)。
输入输出样例 #1
输入 #1
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
输出 #1
11
说明/提示
对于 \(30\%\) 的数据,保证 \(N\leq100\)
对于 \(100\%\) 的数据,保证 \(N\leq100000\)
对于所有的数据,保证 \(K\leq100000, 1\leq X\leq5, 1\leq A, B\leq N\)
\(\text{upd 2022.7.6}\):新添加 \(21\) 组 Hack 数据。
这道题是一道典型的差分约束题,由于图不一定是连通的,所以我们需要建一个零点使他与所有点连接,边权为零,其次对于x=2和x=4时a和b肯定不能相等,所以我们可以在输入时就能直接得出答案,这样可以减少计算量,但我还是不能通过全部的hack
#include<iostream>
#include<queue>
#include<vector>
#define int long long
using namespace std;
char* p1, * p2, buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x = 0, f = 1;
char ch = nc();
while (ch < 48 || ch>57)
{
if (ch == '-')
f = -1;
ch = nc();
}
while (ch >= 48 && ch <= 57)
x = x * 10 + ch - 48, ch = nc();
return x * f;
}
const int N=1e6+10;
struct node{
int id,dis;
};
int cnt[N];
int vis[N];
vector<node>vc[N];
int w[N];
int n,k;
void spfa(){
queue<int>q;
for(int i=1;i<=n;i++){
vc[0].push_back({i,0});
}
for(int i=0;i<=n;i++)w[i]=1e9;
w[0]=0;
for(int i=0;i<=n;i++)q.push(i),vis[i]=1;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<vc[u].size();i++){
int v=vc[u][i].id;
if(w[u]+vc[u][i].dis<w[v]){
w[v]=w[u]+vc[u][i].dis;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n+1){
cout<<"-1";
return;
}
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
int sum=0;
for(int i=1;i<=n;i++)sum+=-w[i];
cout<<sum+n;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
while(k--){
int x,a,b;
cin>>x>>a>>b;
if(x==1)vc[a].push_back({b,0}),vc[b].push_back({a,0});
if(x==2){
if(a==b){
cout<<"-1";
return 0;
}
vc[a].push_back({b,-1});
}
if(x==3)vc[b].push_back({a,0});
if(x==4){
if(a==b){
cout<<"-1";
return 0;
}
vc[b].push_back({a,-1});
}
if(x==5)vc[a].push_back({b,0});
}
spfa();
return 0;
}
补:
双端队列优化,用别人博客的优化不行,用deepseek的就行,也不知道这是什么原理,出题人故意卡spfa真恶心
#include<iostream>
#include<queue>
#include<deque>
#include<vector>
#define int long long
using namespace std;
char* p1, * p2, buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x = 0, f = 1;
char ch = nc();
while (ch < 48 || ch>57)
{
if (ch == '-')
f = -1;
ch = nc();
}
while (ch >= 48 && ch <= 57)
x = x * 10 + ch - 48, ch = nc();
return x * f;
}
const int N=1e6+10;
struct node{
int id,dis;
};
int cnt[N];
int vis[N];
vector<node>vc[N];
int w[N];
int n,k;
void spfa(){
deque<int>q;
for(int i=1;i<=n;i++){
vc[0].push_back({i,1});
}
for(int i=0;i<=n;i++)w[i]=-1e9;
w[0]=0;
for(int i=0;i<=n;i++)q.push_back(i),vis[i]=1;
while(!q.empty()){
int u=q.front();
q.pop_front();
vis[u]=0;
for(int i=0;i<vc[u].size();i++){
int v=vc[u][i].id;
if(w[u]+vc[u][i].dis>w[v]){
w[v]=w[u]+vc[u][i].dis;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n+1){
cout<<"-1";
return;
}
if(!vis[v]){
if(!q.empty()&&w[v]>w[q.front()]){
q.push_front(v);
}
else q.push_back(v);
vis[v]=1;
}
}
}
}
int sum=0;
for(int i=1;i<=n;i++)sum+=w[i];
cout<<sum;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
while(k--){
int x,a,b;
cin>>x>>a>>b;
if(x==1)vc[a].push_back({b,0}),vc[b].push_back({a,0});
if(x==2){
if(a==b){
cout<<"-1";
return 0;
}
vc[a].push_back({b,1});
}
if(x==3)vc[b].push_back({a,0});
if(x==4){
if(a==b){
cout<<"-1";
return 0;
}
vc[b].push_back({a,1});
}
if(x==5)vc[a].push_back({b,0});
}
spfa();
return 0;
}

浙公网安备 33010602011771号