写在前面
先提供一组 hack 数据:
2
3 2 4 2
10 10 10 20
0.3 0.5 0.7 0.4
0.2 0.6 0.2 0.2
0.7 0.1 0.8 0.2
0.5 0.5 0.5 0.5
0.2 0.5 0.3 0.6
0.3 0.5 0.4 0.1
1 2 1
2 3 1
9 3 1 1
36
0.6
0.7
0.1
0.3
0.3
0.9
0.9
0.2
0.9
0.1
0.3
0.9
0.1
0.7
0.6
0.3
0.7
0.8
0.4
0.5
0.5
0.5
0.1
0.5
0.5
0.3
0.5
3 5 1
正确输出:
-1
100.800000
此数据 hack 掉了以下两篇题解:
- https://www.luogu.com.cn/article/rtokwzol
- https://www.luogu.com.cn/article/l3dxtw40
均为清空问题,未将 \(f_{i,j,p+1}\) 赋为 \(0\) 导致第二个结果不正确,更改后即可通过。
上面两篇题解讲的还是不错的,建议不必撤下,但是第一篇甚至是一楼,所以对拍的时候要小心(做题时拍了半天都对不上结果交上去过了),而以上题解均可通过猜测是因为数据中不存在后面的 \(p\) 小于前面的 \(p\) 的情况。
多倍经验:P3227 [HNOI2013] 切糕。
思路
一个选手必须且只能选择一套题,所以可以直接求出选手 \(i\) 答第 \(j\) 套题时的期望:
考虑没有限制时,只需对于每个选手链接一条长度为 \(m+1\) 的链跑最小割即可,具体的,连接 \(s\xrightarrow{inf} (i,1),(i,m)\xrightarrow{val_{i,m}} t,(i,j)\xrightarrow[j\in [1,m)]{val_{i,j}} (i,j+1)\)。
加入限制时,还需引入一个新的节点 \((i,m+1)\),于是变成了连接 \(s\xrightarrow{inf} (i,1),(i,m+1)\xrightarrow{inf} t,(i,j)\xrightarrow[j\in\text{[1,m]}]{val_{i,j}} (i,j+1)\),后面讲为什么要这么做。
对于每一条限制,连接 \((j,h)\xrightarrow[h\in \text{[max(1,i-k),m]}]{inf}(i,\min(h+k,m+1))\) 这样一条无法割开的边,此时如果选择不符合规范时其依然可以从这天边流过去到达 \(t\)。
那么 \((i,m+1)\) 的作用就凸显出来了,我们不能让 \(h+k>m\) 的成为法外狂徒,同时还避免了 \(p=1\) 时的一些不必要的麻烦。
最后跑一遍最小割就好了。
一些细节
-
首先这道题是多测,所以注意清空问题,不光是建图需要清空,还有「写在前面」中提到的 \(f_{i,j,p+1}=0\) 的问题。
-
注意 \(k\) 可能是负数,所以对于限制建边 \(h\) 不一定从 \(1\) 开始。
-
因为是浮点数,所以注意精度问题,要开 eps。
-
对于判断无解,当流量 \(\ge inf\) 时就是无解了。
代码
#include<bits/stdc++.h>
#define ll long long
#define mkp make_pair
using namespace std;
const int N=6510,M=2e5+10,inf=0x3f3f3f3f; const double eps=1e-8;
#ifdef __linux__
#define gc getchar_unlocked
#define pc putchar_unlocked
#else
#define gc _getchar_nolock
#define pc _putchar_nolock
#endif
inline bool blank(const char x) {return !(x^32)||!(x^10)||!(x^13)||!(x^9);}
template<typename Tp> inline void read(Tp &x) {x=0; register bool z=true; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=(x<<1)+(x<<3)+(a^48); x=(z?x:~x+1);}
inline void read(double &x) {x=0.0; register bool z=true; register double y=0.1; register char a=gc(); for(;!isdigit(a);a=gc()) if(a=='-') z=false; for(;isdigit(a);a=gc()) x=x*10+(a^48); if(a!='.') return x=z?x:-x,void(); for(a=gc();isdigit(a);a=gc(),y/=10) x+=y*(a^48); x=(z?x:-x);}
inline void read(char &x) {for(x=gc();blank(x)&&(x^-1);x=gc());}
inline void read(char *x) {register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) *x++=a; *x=0;}
inline void read(string &x) {x=""; register char a=gc(); for(;blank(a)&&(a^-1);a=gc()); for(;!blank(a)&&(a^-1);a=gc()) x+=a;}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y) {read(x),read(y...);}
template<typename Tp> inline void write(Tp x) {if(!x) return pc(48),void(); if(x<0) pc('-'),x=~x+1; register int len=0; register char tmp[64]; for(;x;x/=10) tmp[++len]=x%10+48; while(len) pc(tmp[len--]);}
inline void write(const double x) {register int a=6; register double b=x,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); pc('.'); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);}
inline void write(const pair<int,double>x) {register int a=x.first; if(a<7) {register double b=x.second,c=b; if(b<0) pc('-'),b=-b,c=-c; register double y=5*powl(10,-a-1); b+=y,c+=y; register int len=0; register char tmp[64]; if(b<1) pc(48); else for(;b>=1;b/=10) tmp[++len]=floor(b)-floor(b/10)*10+48; while(len) pc(tmp[len--]); a&&(pc('.')); for(c*=10;a;a--,c*=10) pc(floor(c)-floor(c/10)*10+48);} else cout<<fixed<<setprecision(a)<<x.second;}
inline void write(const char x) {pc(x);}
inline void write(const bool x) {pc(x?49:48);}
inline void write(char *x) {fputs(x,stdout);}
inline void write(const char *x) {fputs(x,stdout);}
inline void write(const string &x) {fputs(x.c_str(),stdout);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y) {write(x),write(y...);}
int T,n,m,h,d,s,t,a[85],dep[N],now[N];
int tot=1,head[N],nxt[M],to[M]; double ans,f[85],p[85],w[M];
inline int id(int x,int y) {return (x-1)*(m+1)+y;}
inline void add(int x,int y,double z)
{nxt[++tot]=head[x],to[head[x]=tot]=y,w[tot]=z;nxt[++tot]=head[y],to[head[y]=tot]=x,w[tot]=0;
}
inline bool bfs()
{queue<int>q; memset(dep,0x3f,sizeof(dep));for(memcpy(now,head,sizeof(now)),q.push(s),dep[s]=0;!q.empty();){int x=q.front(); q.pop();for(int i=head[x],y;y=to[i];i=nxt[i]) if(w[i]>eps&&dep[y]==inf){dep[y]=dep[x]+1,q.push(y); if(y==t) return true;}} return false;
}
inline double dfs(int x,double sum)
{if(x==t||sum<eps) return sum; double sur,res=0;for(int &i=now[x],y;y=to[i];i=nxt[i]) if(w[i]>eps&&dep[y]==dep[x]+1){if((sur=dfs(y,min(sum,w[i])))<eps) {dep[y]=inf; continue;}w[i]-=sur,w[i^1]+=sur,res+=sur; if((sum-=sur)<eps) break;} return res;
}
signed main()
{for(p[0]=1,read(T);T--;tot=1,memset(head,0,sizeof(head))){read(n,m,h,d),t=(s=id(n,m+1)+1)+1,ans=0,f[h+1]=0;for(int i=1;i<=h;i++) read(a[i]),a[i]+=a[i-1];for(int i=1,j,k;i<=m;i++) for(j=1;j<=n;j++){double x=0; for(k=1;k<=h;k++) read(f[k]),p[k]=p[k-1]*f[k];for(k=1;k<=h;k++) x+=p[k]*(1-f[k+1])*a[k];add(id(j,i),id(j,i+1),x);}for(int i=1;i<=n;i++) add(s,id(i,1),inf),add(id(i,m+1),t,inf);for(int i,j,k,o;d--;) for(read(i,j,k),o=max(1,1-k);o<=m;o++)add(id(j,o),id(i,min(o+k,m+1)),inf);while(bfs()&&inf-ans>eps) ans+=dfs(s,inf);inf-ans<eps?write("-1\n"):write(ans,'\n');}
}