NKOJ-2112 糖果

17 篇文章 0 订阅

P2112【SCOI2011 Day1】糖果

时间限制 : 10000 MS 空间限制 : 165536 KB
问题描述

幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,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个小朋友分到的糖果;

输出格式

输出一行,表示lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出-1。

样例输入

5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1

样例输出

11

提示

对于30%的数据,保证 N<=100
对于100%的数据,保证 N<=100000
对于所有的数据,保证 K<=100000,1<=X<=5,1<=A, B<=N

[解析]
这道题是一道差分解不等式(并不知道自己的表达对不对)的题
先分析输入
假设两个小朋友拿到的糖分别是a和b

x=1:a=   b    --->a-b>=0&&b-a>=0
x=2:a<   b    --->b-a>0
x=3:a>=b   --->a-b>=0
x=4:a>   b    --->a-b>0
x=5:a<=b   --->b-a>=0

====>

转换为图论思想(详见差分,虽然我不一定会更)

x=1 : len(a,b)=0
     len(b,a)=0
x=2 : len(b,a)=1
x=3 : len(a,b)=0
x=4 : len(a,b)=1
x=5 : len(b,a)=0
(为什么存正边不存负边呢??稍后解释)

由于n的数据庞大,所以这道题不能暴力存边,要用链式储存(我存的边)

这样一来图就建好一半了(差了个原点)

接下来解释为什么要存正而不存负(假如你没看懂这句话,请你滚回去看前面)
我们假设一下存的是负

那么每一个点到原点的长度都是负的————所以我们要存回正的
取绝对值是不行的(这个问题很简单就能想明白,比如自己举个例子)
那我们就找到最小的负值,以它为基准加一个数,是每个点的值都为正的吧!(好呀好呀)
这一看上面这个方法是对的,但是其实是错的
如果这个幼儿园的小朋友是分班的呢(废话正常的幼儿园有十万个学生怎么会不分班??)
意思是,在不加入原点的情况下,这个图如果是不连通的呢??
我们一个不连通的图看做两个单独的图,一个图的最小值是-1,另一个是-5
假如我们将他们一起讨论,第一个图每一个点就明显多加了4(然后幼儿园就亏本了,这个问题很严重的对吧)

所以,我们决定(主要是幼儿园领导决定的),一开始计算的时候就存正值

然后就是关于最短路的初值和不同之处的修改:
我们要加一个原点,使得最后的结果更好计算,也使整个图连在一起
这个点存为0是不行的,因为图里头所有的点的nxt的最后一个值都是0,加入原点存为0,那么这个图就一定存在环了
所以我们存原点为n+1(其实这是幼儿园院长混进去偷糖吃),然后将原点到每一条边的边长都设为1,这样,第一遍之后每个小朋友就都有一个糖啦!
然后就正常地SPFA(但听说这道题卡SPFA,所以我们一会再讨论怎么算)

在仔细地了解了情况之后,我终于知道什么叫卡SPFA了…
十组数据当中,有一组数据假如你顺序(1-n)添加原点到每一个点的连线的话,结果就会被卡,好像正好形成一个n-1次的SPFA,就是正好不构成正权回路,但又卡的你想哭的那种…(很不让人省心的一群小朋友,小小年纪就互相陷害,还让幼儿园老师这么尴尬)
所以,机智的我们决定从(n-1)添加原点到每个点的连线(就是这么简单…不要问我为什么…因为我也不知道…周末可以就这个进行讨论)

然后输出结果就可以了

附上代码

#include <iostream>
#include <cstdio>
using namespace std;

int go[10000005],org,a,b,m,n,t,starts[1234567],eds[1234567],len[1234567],nxt[1234567],ban=0,count[1234567],dis[123456];
long long res=0;
bool used[1234567],j;

void _r(int &d)
{
    d=0;
    char t=getchar();
    for(d=0;t>='0'&&t<='9';t=getchar())d=(d<<3)+(d<<1)+t-'0';
}

void add(int s,int e,int l)//加边函数,有可能会增加运算量,但是这样看着就感觉思路清晰一些,所以我不管
{
    ban++;
    nxt[ban]=starts[s];
    starts[s]=ban;
    eds[ban]=e;
    len[ban]=l;
}

void Pro()//SPFA
{
    int x,s,e,l;
    int head=1,tail=2;
    go[1]=n+1;used[n+1]=1;
    while(head!=tail)
    {
        x=go[head];s=starts[x];head++;used[x]=0;
        while(s)
        {
            l=len[s];
            e=eds[s];
            if(dis[e]<dis[x]+l)
            {
                dis[e]=dis[x]+l;
                if(!used[e])
                {
                    count[e]++;
                    if(count[e]==n){j=1;return;}
                    used[e]=1;
                    go[tail]=e;
                    tail++;
                }
            }
            s=nxt[s];
        }
    }

}

int main()
{
    _r(n);_r(t);
    org=n+1;
    for(int i=n;i>=1;i--)add(org,i,1);//逆向加入边,避免卡SPFA
    for(int i=1;i<=t;i++)
    {
        _r(m);_r(a);_r(b);
        if(m==1)add(a,b,0),add(b,a,0);
        if(m==2)
        {
            if(a==b){puts("-1");return 0;}
            add(a,b,1);
        }
        if(m==3)add(b,a,0);
        if(m==4)
        {
            if(a==b){puts("-1");return 0;}
            add(b,a,1);
        }
        if(m==5)add(a,b,0);
    }
    Pro();
    if(j){cout<<-1;return 0;}
    for(int i=1;i<=n;i++)res+=dis[i];
    cout<<res;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值