传送门 >Here<
题意:给出N段区间,并告诉你每段区间里有几个数(一个位置只能放一个数) 问总共至少有几个数
解题思路
差分约束题,本蒟蒻也是第一次做差分约束题……
所谓差分约束,常常是通过最短路(或最长路)来解决一些约束问题,例如不等式组
举个例子:$$x1 -x2 leq a1 (1)$$$$x2 -x3 leq a2 (2)$$$$x1 -x3 leq a3 (3)$$求解$x1-x3$的解集
则我们可以让1~2连一条长度为a1的有向边,2~3连一条长度为a2的有向边,1~3连一条长度为a3的有向边
我们会发现$(1)+(2)$可以得到$x1 - x3 leq a1 + a2$(利用了不等式之间相加的定理),因此得到一组不等式组$$x1 - x3 leq a1 + a2$$$$x1 -x3 leq a3 (3)$$
而最终由于是小于等于的约数,解集应当取$Min{ a1+a2, a3 }$
为什么可以转化为最短路问题呢?考虑一条路径,对于所有非起点且非终点的点,与其相邻的路径上的两个点对应了两条路径,不妨设为$a-s,s-b$,则当这两个不等式相加时,中介点$s$一定会被消掉。宏观来看,一条路径上所有非起点终点的点都将为被抵消掉。因此最后只剩下路径的头尾了。而要求解集,如果是$<$,则应该取最小值,也就是最短路了。
以上是差分约束的基本概念。那么回到这道题来,好像和差分约束没什么关系?
光看好像是没什么关系。由于是考虑区间内数的个数,不妨设想有一个前缀和数组s,这样$[a_i, b_i]$至少有$c_i$个元素就可以表示成$s[b_i] - s[a_i-1] >= c[i]$,我们会得到若干个这样的不等式,就可以做差分约束了——然而值得注意的是,刚才的例子里是小于等于,而这里是大于等于。所以这里的解集应当取到最大,所以求的是最长路而不是最短路
真的仅仅只是这样吗?我们忽略了题目给的一个条件——每个位置只能放一个数,所以我们的约束条件少了,要加上对于每一个位置i,$s[i]-s[i-1] geq 0, s[i]-s[i-1] leq 1$(其实$s[i]-s[i-1]$)就是i这个位置的数的个数,不是0就是1. 对于小于等于的情况,同时乘以-1转换成大于等于的形式,$s[i-1]-s[i] geq -1$即可
code
注意Dijkstra是不能做负权的(有$s[i-1]-s[i] geq -1$的存在),所以用SPFA
/*By QiXingzhi*/ #include <cstdio> #include <cstring> #include <queue> #include <algorithm> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int MAXN = 500010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); if(c == '-') w = -1, c = getchar(); while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w; } int N,L,R; int a,b,c,d[MAXN],vis[MAXN]; int first[MAXN*2],nxt[MAXN*2],to[MAXN*2],cost[MAXN*2],num_edge; queue <int> q; inline void add(int u, int v, int w){ // printf("%d->%d (%d) ", u,v,w); to[++num_edge] = v; cost[num_edge] = w; nxt[num_edge] = first[u]; first[u] = num_edge; } inline void SPFA(int s){ for(int i = 0; i <= N; ++i) d[i] = -INF; d[s] = 0; q.push(s); int u, v; while(!q.empty()){ u = q.front(); q.pop(); vis[u] = 0; for(int i = first[u]; i; i = nxt[i]){ v = to[i]; if(d[u] + cost[i] > d[v]){ d[v] = d[u] + cost[i]; if(!vis[v]){ vis[v] = 1; q.push(v); } } } } } inline void Init(){ memset(vis,0,sizeof(vis)); memset(first,0,sizeof(first)); memset(nxt,0,sizeof(nxt)); memset(to,0,sizeof(to)); memset(cost,0,sizeof(cost)); } int main(){ // freopen(".in","r",stdin); while(scanf("%d",&N) == 1){ L = N+1, R = -1; Init(); for(int i = 1; i <= N; ++i){ a=r,b=r,c=r; add(a, b+1, c); L = Min(L, a); R = Max(R, b+1); } for(int i = 2; i <= R; ++i){ add(i+1, i, -1); add(i, i+1, 0); } SPFA(L); // printf("R+1 = %d L+1 = %d ",R+1,L+1); printf("%d ", d[R]); } return 0; }