题目大意:
有N≤3e3个格子,你可以任意给每个格子染色,但是要满足M≤3e3限制条件,限制条件有两种类型:
1. 区间[l,r]中被染色的格子数量不少于K。
2. 区间[l,r]外被染色的格子数量不少于K。
在满足所有限制条件下求染色格子数量的最小值。
题解:算作是差分约束+二分的板子题,比赛时还不知道差分约束是什么,当时一点思路都没有,赛后看了大牛们的讲解才理解,其实就是把一些不等式问题转换成最短路或者最长路,不懂得可以看看这篇文章:http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html
写的是真的好,里面讲的一道例题就和这个题差不多。
首先因为染色格子的数量是越多越好,即满足递增的,那么我们就可以二分枚举染色格子的总数量mid
我们很容易能写出以下不等式(注意那些隐藏条件,因为差分约束正确的前提是满足所有条件):
然后我们分别建图判断是否存在0-n的最短路就行了。
trick:这个题有点卡常,用spfa时需要剪枝一下,实测仅用SLF优化也可以过,不过有个更牛皮的优化,具体内容参考代码
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 3e3+7; struct edge{//链式前向星存图 int u,v,w,nxt; int flag;//flag为1记做+delta,为2记做-delta,方便进行改变枚举的值的影响 }e[N*10]; int head[N],edgeCnt,dis[N]; deque<int> q; int cnt[N],inq[N];//常规的SPFA所需的数组 int n,m1,m2,T; inline void addEdge(int u,int v,int w,int flag){//建有向边 e[++edgeCnt]={u,v,w,head[u],flag}; head[u]=edgeCnt; } inline void add(int delta){//修改枚举值对边的影响 rp(i,1,edgeCnt){ if(e[i].flag==1) e[i].w+=delta; if(e[i].flag==2) e[i].w-=delta; } } int Spfa(int delta){//spfa判断是否有负环 add(delta);//加上枚举值的影响 while(!q.empty()) q.pop_front(); rp(i,0,n){ dis[i]=(i==0)?0:INF; cnt[i]=inq[i]=0; } q.push_back(0); inq[0]=1; while(!q.empty()){ int now=q.front(); q.pop_front(); inq[now]=0; if(cnt[now]++>n){ add(-delta);//减去枚举值的影响 return 0; } //大力剪枝,如果有一个点的最短路为负,则必定存在负环 //因为 i->i-1 有一条权值为0的边,而且是从0为起点走的,那么就可以通过走序号不断减1的边回到0,这样就形成了一个负环 if(dis[now]<0){ add(-delta); return 0; } for(int i=head[now];i;i=e[i].nxt){ if(dis[e[i].v]>dis[now]+e[i].w){ dis[e[i].v]=dis[now]+e[i].w; if(!inq[e[i].v]){ inq[e[i].v]=1; if(!q.empty()&&dis[e[i].v]<dis[q.front()])//比较常见的SLF优化 q.push_front(e[i].v); else q.push_back(e[i].v); } } } } add(-delta); return 1; } void init(){//初始化 mst(head,0); edgeCnt=0; } int main(){ T=read(); while(T--){ init(); n=read(),m1=read(),m2=read(); while(m1--){ int l=read(),r=read(),k=read(); //S(r)-S(l-1)>=k => S(l-1)-S(r)<=-k addEdge(r,l-1,-k,0); } while(m2--){ int l=read(),r=read(),k=read(); //S(r)-S(l-1)<=mid-k addEdge(l-1,r,-k,1); } rp(i,1,n){ //S(i)-S(i-1)<=1 //S(i)-S(i-1)>=0 => S(i-1)-S(i)<=0 addEdge(i-1,i,1,0); addEdge(i,i-1,0,0); } //S(n)-S(0)==mid => S(n)-S(0)>=mid and S(n)-S(0)<=mid //S(n)-S(0)>=mid => S(0)-S(n)<=-mid addEdge(0,n,0,1); addEdge(n,0,0,2); int l=0,r=n,ans=-1; while(l<=r){//二分枚举符合条件的值 int mid=(l+r)>>1; // cout<<mid<<" "<<Spfa(mid)<<endl; if(Spfa(mid)){ ans=mid; r=mid-1; } else l=mid+1; } printf("%d\n",ans); } return 0; }
2019CCPC哈尔滨A题——差分约束系统+二分
最新推荐文章于 2021-11-25 11:50:23 发布