[NOIP2017模拟]证明

来源:互联网 发布:互联网大数据技术 编辑:程序博客网 时间:2024/05/22 13:22

题目背景
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;}

本题结。

原创粉丝点击