题目背景
SOURCE:NOIP2015-HN-CJZX
题目描述
H 教授是一位德高望重的教授,也是计算机科学界的权威。他对问题总有独特而深刻的见解,治学严谨,是学术界的带头人。
在一次科学家大会上,H 教授在黑板上写下了 n 个式子 x1,x2,…,xn ,并向参加会议的所有科学家证明了:如果 x1=x2=…=xn ,那么可以证明 P=NP 。可是,毕竟人无完人,H 教授对其中的任意两个式子是否相等都说不清。他把这个问题抛给了全世界的科学家们。
令人激动的是,没过多久,H 教授就收到了数学家们发来的 m 封 email ,第 i 封 email 写到,发信人已经证明了 ∀li≤a<ri,xa=xa+1 ,即 xli,x(li)+1,…,xri 两两相等。但是,这些证明是有版权的,如果 H 教授需要使用这些证明,那么需要向提供证明的人支付 ci 元稿费。
H 教授希望通过这些信息证明出 P=NP 。但是,H 教授最近手头拮据,所以希望支付最小的费用。
输入格式
输入的第一行是两个整数 n,m。
接下来 m 行,每行三个整数
li,ri,ci(1≤li≤ri≤n)
,代表第 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;
}
本题结。