【LuoguP4926】倍殺測量者-二分答案+差分約束+判正環
阿新 • • 發佈:2018-12-16
測試地址:倍殺測量者 做法: 本題需要用到二分答案+差分約束+判正環。 對於第一種flag,用不等式表示:如果滿足就不用女裝;對於第二種flag,用不等式表示:如果滿足就不用女裝。顯然我們可以二分(因為顯然越大這些條件更容易滿足),轉化成判定這些條件能不能同時合法的問題,那麼使得這些條件同時滿足的最小的就可以近似地看作使得存在一個條件滿足的最大的了。 但是我們發現這些條件很難直接去判斷合不合法,這時候我們想到一個判斷這種兩變數間不等式組合不合法的一個圖論模型:差分約束系統,但這個題的條件形式不是差分,這怎麼辦呢? 我們知道,而且也是單調增的函式,所以我們萌生一個大膽的想法:將上面的所有條件都取,於是上面的條件就變成: ; 因為這些變數都是在實數域內的,因此可以近似地看作,這就符合差分約束系統的使用條件了。於是我們把差分約束系統建出來,然後用SPFA演算法判正環即可。時間複雜度為(表示二分的複雜度)。 現在還有幾個遺留的問題。首先,已知變數怎麼處理?我們可以建立一個參照點,把這一點當成相對的,那麼就等價於和兩個條件,在差分約束系統中建邊即可。 然後就是源點不確定,建一個超級源點向所有點連邊即可。 還有就是判斷無解的問題。因為要是正實數,所以如果時所有條件就都滿足了,那顯然就不可能存在使得存在一個條件不滿足了,特判一下即可。 最後注意一下的二分範圍,因為在時沒有意義,所以的最大值應該被設為上述第一種flag中的最小值。如果沒有第一種flag…那上界設到就夠了(雖然我也不知道為啥就夠了)。 以下是本人程式碼:
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-6;
const double inf=1000000000;
int n,s,t;
int o[1010],a[1010],b[1010];
int c[1010];
double k[1010],x[1010];
int first[1010]={0},tot=0;
struct edge
{
int v,next;
double w;
}e[20010];
int vis[1010],inque[1010];
double dis[1010];
queue<int> Q;
void insert(int a,int b,double w)
{
e[++tot].v=b;
e[tot].next=first[a];
e[tot].w=w;
first[a]=tot;
}
bool spfa(int s)
{
while(!Q.empty()) Q.pop();
Q.push(s);
dis[s]=0;
inque[s]=1;
while(!Q.empty())
{
int v=Q.front();Q.pop();
vis[v]++;
inque[v]=0;
if (vis[v]>(n+2)) return 0;
for(int i=first[v];i;i=e[i].next)
if (dis[e[i].v]<dis[v]+e[i].w)
{
dis[e[i].v]=dis[v]+e[i].w;
if (!inque[e[i].v])
{
inque[e[i].v]=1;
Q.push(e[i].v);
}
}
}
return 1;
}
bool check(double T)
{
tot=0;
memset(first,0,sizeof(first));
for(int i=1;i<=s;i++)
{
if (o[i]==1) insert(b[i],a[i],log2(k[i]-T));
else insert(b[i],a[i],-log2(k[i]+T));
}
for(int i=1;i<=t;i++)
{
insert(n+1,c[i],log2(x[i]));
insert(c[i],n+1,-log2(x[i]));
}
for(int i=1;i<=n+1;i++)
insert(n+2,i,0.0);
for(int i=1;i<=n+2;i++)
dis[i]=-inf;
memset(vis,0,sizeof(vis));
memset(inque,0,sizeof(inque));
return spfa(n+2);
}
int main()
{
double l=0.0,r=10.0;
scanf("%d%d%d",&n,&s,&t);
for(int i=1;i<=s;i++)
{
scanf("%d%d%d%lf",&o[i],&a[i],&b[i],&k[i]);
if (o[i]==1) r=min(r,k[i]);
}
for(int i=1;i<=t;i++)
scanf("%d%lf",&c[i],&x[i]);
if (check(0)) printf("-1");
else
{
while(r-l>eps)
{
double mid=(l+r)/2.0;
if (check(mid)) r=mid;
else l=mid;
}
printf("%lf",l);
}
return 0;
}