[NOIP2017模拟]证明

题目背景
SOURCE:NOIP2015-HN-CJZX

题目描述
H 教授是一位德高望重的教授,也是计算机科学界的权威。他对问题总有独特而深刻的见解,治学严谨,是学术界的带头人。

在一次科学家大会上,H 教授在黑板上写下了 n 个式子 x1x2xn ,并向参加会议的所有科学家证明了:如果 x1=x2==xn ,那么可以证明 P=NP 。可是,毕竟人无完人,H 教授对其中的任意两个式子是否相等都说不清。他把这个问题抛给了全世界的科学家们。

令人激动的是,没过多久,H 教授就收到了数学家们发来的 m 封 email ,第 i 封 email 写到,发信人已经证明了 lia<rixa=xa+1 ,即 xlix(li)+1xri 两两相等。但是,这些证明是有版权的,如果 H 教授需要使用这些证明,那么需要向提供证明的人支付 ci 元稿费。

H 教授希望通过这些信息证明出 P=NP 。但是,H 教授最近手头拮据,所以希望支付最小的费用。

输入格式
输入的第一行是两个整数 n,m。
接下来 m 行,每行三个整数 lirici(1lirin) ,代表第 i 位数学家的证明及其稿费。

输出格式
输出只包含一个整数,表示 H 教授至少要支付多少元稿费,才能证明出 P=NP。如果根据现有条件无法证明 P=NP ,请输出-1。

样例数据 1
输入
9 3
1 3 101010
4 6 98889
7 9 76543
输出
-1
样例数据 2
输入
9 7
1 5 3
3 6 8
5 8 4
4 7 6
2 3 7
7 9 2
6 7 5
输出
9

备注
【样例1说明】
就算把所有的数学家都叫上,仍然证明不了 x3=x4 和 x6=x7。

【样例2说明】
第一位数学家可以证明 x1=x2=x3=x4=x5。
第三位数学家可以证明 x5=x6=x7=x8。
第六位数学家可以证明 x7=x8=x9。
这三位数学家是足够的,并且只需 9 元稿费。可以证明没有更优的方案。

【数据说明】
所有测试点的数据范围如下表所示。
这里写图片描述

分析:对数学家的证明区间排序,离散化后建图,跑dijkstra即可(SPFA要被卡),详见代码。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int f=1,sum=0;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int N=2e5+5,M=1e6+5;
long long INF=1e17;
struct node
{
    int l,r;
    long long c;
}mathest[N];
int n,m,b[N];
int tot,first[N],nxt[M],to[M],w[M];
long long dis[N];
priority_queue<pair<long long,int> >que;

bool comp(const node &a,const node &b)
{
    if(a.l==b.l) return a.r<b.r;
    return a.l<b.l;
}

void lsh()//作用:将很大很大的点的下标前移,如1、4、64、8686、10000变成1、2、3、4、5
{
    n=0;
    for(int i=1;i<=m;++i)
        b[++n]=mathest[i].l,b[++n]=mathest[i].r;

    sort(b+1,b+n+1);

    n=unique(b+1,b+n+1)-b-1;//去重
    for(int i=1;i<=m;++i)
    {
        mathest[i].l=lower_bound(b+1,b+n+1,mathest[i].l)-b;
        mathest[i].r=lower_bound(b+1,b+n+1,mathest[i].r)-b;
    }
}

void addedge(int x,int y,int z)
{
    nxt[++tot]=first[x];
    first[x]=tot;
    to[tot]=y;
    w[tot]=z;
}

void dijkstra()
{
    for(int i=1;i<=n;++i) dis[i]=INF;
    dis[1]=0;
    que.push(make_pair(0,1));
    while(!que.empty())
    {
        int u=que.top().second;
        que.pop();
        for(int p=first[u];p;p=nxt[p])
        {
            int v=to[p];
            if(dis[u]+w[p]<dis[v])
            {
                dis[v]=dis[u]+w[p];
                que.push(make_pair(-dis[v],v));
            }
        }
    }
}

int main()
{
    freopen("proof.in","r",stdin);
    freopen("proof.out","w",stdout);

    n=getint();m=getint();
    for(int i=1;i<=m;++i)
        mathest[i].l=getint(),mathest[i].r=getint(),mathest[i].c=getint();

    sort(mathest+1,mathest+m+1,comp);

    int cnt=mathest[1].r;//先判断能否证明完
    for(int i=2;i<=m;i++)
    {
        if(mathest[i].l>cnt)//断节了
        {
            cout<<-1;
            return 0;
        }
        cnt=max(cnt,mathest[i].r);
    }
    if(cnt<n||mathest[1].l>1)//左右边界没覆盖
    {
        cout<<-1;
        return 0;
    }

    lsh();//离散化

    for(int i=1;i<=m;++i)
        addedge(mathest[i].l,mathest[i].r,mathest[i].c);//把离散化后的左右顶点建边,权值为花费ci
    for(int i=2;i<=n;++i)
        addedge(i,i-1,0);//因为现在的所有数学家能覆盖所有数,
//即被证明了两端点的中间部分是不需要花费的,就是边权为0的边,
//离散化后这些点被删掉了,直接连接了某个区间(i)的右端点和另一个左端点被包含在(i)区间内的区间(j)的左端点,权值为0。
//这样,如果同时选择了这两位数学家,花费才是ci+cj

    dijkstra();

    dis[1]=INF;//如果只证明1,那显然花费不会是我们赋值的0,而是所有证明了1的人给出的最小价格
    for(int i=1;i<=m;++i)
        if(mathest[i].l==1)
            dis[1]=min(dis[1],mathest[i].c);

    cout<<dis[n];
    return 0;
}

本题结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值