2019CCPC哈爾濱Artful Paintings(二分+差分約束)
題目鏈接:傳送門
思路:
這題現場賽的時候TLE了,賽後才發現spfa可以剪枝,而且還缺少一約束。
我們假設答案是k,那麼k+1也可行,所以可行性具有單調性。設函數S( i )爲前 i 個cube畫的個數。
那麼有約束
- 對於第一種條件,
- 對於第二種條件,
因爲對於第二種條件, 的值不確定,所以不能直接建立差分約束圖。但是因爲可行性對於的值有單調性,所以我們二分 的值,就能用差分約束系統來check 的可行性,從而求出的最小值。
但是要注意的是,假設此時令,然後檢查可行性的時,需要增加約束。另外沒有剪紙的差分約束是會TLE的(根據圖的性質進行剪紙),但是建圖(建最短圖的話)後我們發現所有點都有到0有一條費用爲0的路徑,所以一旦存在某個u 使得 ,那麼必定有負環。
代碼:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=3e3+10;
const int inf=0x3f3f3f3f;
struct Node
{
int l,r,k;
} q1[N],q2[N];
int n,k1,k2;
int dis[N],in[N];//距離
struct Edge{
int to,w;
Edge() {};
Edge(int to,int w):to(to),w(w) {}
};
vector<Edge> g[N];//鄰接表
bool book[N];//標記是否夠在隊列
bool spfa(int s,int n)//源點爲s,共有(0..n)n個點,求最短路
{
//有負環返回true,無負環返回false
queue<int> qe;
fill(dis,dis+n,inf);
fill(in,in+n,0);
fill(book,book+n,false);
dis[s]=0;
qe.push(s);
book[s]=true;
while(!qe.empty())
{
int u=qe.front();
qe.pop();
in[u]++;
book[u]=false;
if(dis[u] < 0 ) return true;
if(in[u] > n) return true;
for(Edge &e:g[u])
{
int v=e.to,w=e.w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if(!book[v])
{
book[v]=true;
qe.push(v);
}
}
}
}
// puts("no circle");
return false;
}
bool check(int mv)//無環返回true
{
// printf("mv:%d\n",mv);
for(int i=0; i<=n; ++i) g[i].clear();
for(int i=1; i<=k1; ++i)
{
int u=q1[i].r,v=q1[i].l-1,w=-q1[i].k;
g[u].push_back(Edge(v,w));
}
for(int i=1;i<=n;++i){
g[i].push_back(Edge(i-1,0));
g[i-1].push_back(Edge(i,1));
}
for(int i=1; i<=k2; ++i)
{
int u=q2[i].l-1,v=q2[i].r,w=mv-q2[i].k;
g[u].push_back(Edge(v,w));
}
g[0].push_back(Edge(n,mv));
g[n].push_back(Edge(0,-mv));
return !spfa(0,n+1);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&k1,&k2);
for(int i=1; i<=k1; ++i)
scanf("%d%d%d",&q1[i].l,&q1[i].r,&q1[i].k);
for(int i=1; i<=k2; ++i)
scanf("%d%d%d",&q2[i].l,&q2[i].r,&q2[i].k);
int ans=-1,l=0,r=n;
while(l<=r)
{
int m=(l+r)/2;
if(check(m))
{
ans=m;
r=m-1;
}
else
l=m+1;
}
printf("%d\n",ans);
}
}