题目链接
题意
-
就是有一个长度为 n n n的只含有 0 0 0和 1 1 1的数组,开始你不知道数组的内容,给你两种描述:
- 第一种表示区间 [ l i , r i ] [l_i,r_i] [li,ri]内至少含有 k i k_i ki个 1 1 1
- 第二种表示除去区间 [ l i , r i ] [l_i,r_i] [li,ri]的剩下部分至少含有 k i k_i ki个 1 1 1
然后让你求在满足所有这些给定的限制条件下,整个数组最少含有多少个 1 1 1
题解
- 首先这个题和17CCPC哈尔滨站L有相似之处,只是把树上问题放到了序列上,问题就变复杂了一些
- 为什么变复杂了呢?首先那个题的题解链接先贴上:17CCPC哈尔滨站题解,可以看到为什么那个题可以 d p dp dp,原因就在于那个题实际上是利用了区间包含的特点,而没有区间交叉的情况,因为当前节点的子树一定包含所有以儿子为根节点的子树,所以只有包含关系,当放到序列上时,就会产生交叉情况
- 另外如果你做过POJ3169的话,那么这个题应该能根块想出来做法
- 首先容易想到的是如果答案是 a n s ans ans,只要 a n s < n ans<n ans<n那么 a n s + 1 ans+1 ans+1也一定可以满足给定的所有条件,因为你可以在任意一个空的位置放上一个 1 1 1,所以答案具有单调性,考虑二分,关键是怎么 c h e c k check check的问题,考虑前缀和 s u m sum sum,首先对于第一种描述,可以转化为 s u m [ r i ] − s u m [ l i − 1 ] ≥ k i sum[r_i]-sum[l_i-1]\geq k_i sum[ri]−sum[li−1]≥ki,那么对于第二种描述,考虑转化一下:区间 [ l i , r i ] [l_i,r_i] [li,ri]内的1的个数最多有 m i d − k i mid-k_i mid−ki个,即 s u m [ r i ] − s u m [ l i − 1 ] ≤ m i d − k i sum[r_i]-sum[l_i-1]\leq mid-k_i sum[ri]−sum[li−1]≤mid−ki,然后还有一些隐含的条件那就是 ∀ i ∈ [ 1 , n ] , 0 ≤ s u m [ i ] − s u m [ i − 1 ] ≤ 1 \forall\ i \in [1,n], 0\leq sum[i]-sum[i-1] \leq 1 ∀ i∈[1,n],0≤sum[i]−sum[i−1]≤1,以及 s u m [ n ] − s u m [ 0 ] ≤ m i d sum[n]-sum[0]\leq mid sum[n]−sum[0]≤mid,根据这些约束条件建图(具体建图方法参见挑战 P 111 P111 P111),然后跑 s p f a spfa spfa判断是否有负环,如果有则差分约束系统无解,即二分的 m i d mid mid太小
- 另外有一个剪枝技巧就是如果跑 s p f a spfa spfa中途遇到有某个节点的 d i s dis dis值小于零,那么一定有负环,原因是由于 ∀ i ∈ [ 1 , n ] \forall i \in[1,n] ∀i∈[1,n],节点 i i i向节点 i − 1 i-1 i−1连了一条去哪治为0的边,那么就是说对于任意一个节点 i i i到节点0都有一条最长是0的长度的最短路,那么如果0到某个节点的 d i s dis dis值小于 0 0 0就一定有负环
- 假了这个优化只用了
31
m
s
31ms
31ms就跑完了,目前这份代码的提交在Codeforces的提交是所有提交当中跑的最快的(其中_TLE_和wzw19991105都是我的号)
46
m
s
46ms
46ms的提交用的是C++ STL的
d
e
q
u
e
deque
deque,
31
m
s
31ms
31ms是自己手写了个
d
e
q
u
e
deque
deque
剪枝前是这样的(差一点没过去)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
const int maxm=100005;
int n,m1,m2,head[maxn],tot,dis[maxn],inq[maxn],cnt[maxn];
struct opt{int l,r,w;}a[maxn],b[maxn];
struct edge{int u,v,w,type,next;}e[maxm];
void add_edge(int u,int v,int w,int t) {e[++tot]=edge{u,v,w,t,head[u]};head[u]=tot;}
struct dequeue_{
int a[maxn],L,R,siz=0;
bool empty() {return siz==0;}
dequeue_(int l=1,int r=0,int siz_=0) {L=l,R=r,siz=siz_;}
void clear() {
L=1,R=0,siz=0;
}
bool push_front(int val) {
if(siz==maxn-1) return false;
if(L==1) L=maxn-1,a[L]=val,siz++;
else L--,a[L]=val,siz++;
return true;
}
bool push_back(int val) {
if(siz==maxn-1) return false;
if(R==maxn-1) R=1,a[R]=val,siz++;
else R++,a[R]=val,siz++;
return true;
}
int pop_front() {
if(siz==0) return 0;
int ans=0;
if(L==maxn-1) ans=a[L],L=1,siz--;
else ans=a[L],L++,siz--;
return ans;
}
int pop_back() {
if(siz==0) return 0;
int ans=0;
if(R==1) ans=a[R],R=maxn-1,siz--;
else ans=a[R],R--,siz--;
return ans;
}
int front() {return a[L];}
int back() {return a[R];}
};
bool spfa(int s,int mid) {
for(int i=0;i<=n;i++) dis[i]=0x3f3f3f3f,inq[i]=0,cnt[i]=0;
dequeue_ que;
que.push_front(s);
inq[s]=1,dis[s]=0,cnt[0]=1;
while(!que.empty()) {
int cur=que.front();
que.pop_front();
inq[cur]=0;
for(int i=head[cur];i;i=e[i].next) {
int weight=e[i].type?mid+e[i].w:e[i].w;
if(dis[e[i].v] > dis[e[i].u]+weight) {
dis[e[i].v]= dis[e[i].u]+weight;
if(dis[e[i].v] < 0) return false; //根据建图特点剪枝
if(!inq[e[i].v]) {
if(!que.empty() && dis[e[i].v]>=dis[que.front()]) que.push_back(e[i].v);
else que.push_front(e[i].v); //SLF优化
cnt[e[i].v]++,inq[e[i].v]=1;
if(cnt[e[i].v]>n) return false;
}
}
}
}
return true;
}
bool check(int mid) {
e[tot].w=-mid,e[tot-1].w=mid; //修改这两条边的权值
return spfa(0,mid);
}
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d %d %d",&n,&m1,&m2);
for(int i=1;i<=m1;i++) scanf("%d %d %d",&a[i].l,&a[i].r,&a[i].w);
for(int i=1;i<=m2;i++) scanf("%d %d %d",&b[i].l,&b[i].r,&b[i].w);
for(int i=1;i<=n;i++) add_edge(i,i-1,0,0),add_edge(i-1,i,1,0);
for(int i=1;i<=m1;i++) add_edge(a[i].r,a[i].l-1,-a[i].w,0);
for(int i=1;i<=m2;i++) add_edge(b[i].l-1,b[i].r,-b[i].w,1);
add_edge(0,n,0,0),add_edge(n,0,0,0);
int l=0,r=n,ans=-1; //二分答案
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d\n",ans);
for(int i=0;i<=n;i++) head[i]=0; //清空
tot=0;
}
}