A - CF605E Intergalaxy Trips
Sol
设 i i i号点到达 n n n需要的期望天数是 f i f_i fi。
每个点的策略一定是:钦定一个集合 S S S,等到 S S S里面至少有一个点和它连通的时候,走 S S S中的、与它连通的、到 n n n的期望距离最小的点。显然不在 S S S中的点到 n n n的期望距离要大于 S S S中的点,否则这种钦定 S S S的方法一定是不优秀的。
对于一个点
u
u
u来说,如果它选的集合是
S
S
S,那么有
f
u
=
1
+
f
u
⋅
∏
x
∈
S
(
1
−
p
u
,
x
)
+
∑
x
∈
S
f
x
p
u
,
x
⋅
∏
y
∈
S
,
f
y
<
f
x
(
1
−
p
u
,
y
)
f_u = 1 + f_u \cdot \prod_{x\in S} (1-p_{u,x}) + \sum_{x\in S} f_xp_{u,x} \cdot \prod_{y\in S,f_y< f_x}(1-p_{u,y})
fu=1+fu⋅x∈S∏(1−pu,x)+x∈S∑fxpu,x⋅y∈S,fy<fx∏(1−pu,y)
也就是
f
u
=
1
+
∑
x
∈
S
f
x
p
u
,
x
⋅
∏
y
∈
S
,
f
y
<
f
x
(
1
−
p
u
,
y
)
1
−
∏
x
∈
S
(
1
−
p
u
,
x
)
f_u = {1+\sum_{x\in S} f_xp_{u,x} \cdot \prod_{y\in S,f_y< f_x}(1-p_{u,y}) \over1-\prod_{x\in S} (1-p_{u,x})}
fu=1−∏x∈S(1−pu,x)1+∑x∈Sfxpu,x⋅∏y∈S,fy<fx(1−pu,y)
显然 ∀ x ∈ S , f u > f x \forall x\in S,f_u > f_{x} ∀x∈S,fu>fx。
故而可以模拟dijkstra算法的过程,每次找出 f u f_u fu最小的点,然后用它去更新其它点,看把它加入 S S S中会不会更优秀。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define db long double
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=1010;
db sum[N],pro[N],dis[N];
int p[N][N],vis[N],n;
int to[N];
void work(int u,int flg=1) {
vis[u]=1;
dis[u]=(sum[u]+flg)/(1-pro[u]);
// cout<<u<<':'<<dis[u]<<endl;
for(int v=1;v<=n;++v) if(!vis[v]) {
if(p[v][u]==0) continue;
// cout<<"upd:"<<v<<endl;
if(!to[v]) {
sum[v]=dis[u]*p[v][u]/100.0;
pro[v]=(100-p[v][u])/100.0;
to[v]=1;
continue;
}
db tmp1=(sum[v]+1)/(1-pro[v]);
db tmp2=(sum[v]+pro[v]*p[v][u]/100.0*dis[u]+1)/(1-pro[v]*(100-p[v][u])/100.0);
if(tmp2<tmp1) sum[v]+=pro[v]*p[v][u]/100.0*dis[u],pro[v]*=(100-p[v][u])/100.0;
}
}
int main() {
rd(n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
rd(p[i][j]);
if(n==1) {
printf("0");
return 0;
}
work(n,0);
while(1) {
int p=0; db mi=1e20;
for(int i=1;i<=n;++i) if(!vis[i]&&to[i]) {
db tmp=(sum[i]+1)/(1-pro[i]);
if(!p||tmp<mi) mi=tmp,p=i;
}
if(p==1) {
printf("%.20Lf\n",(sum[1]+1)/(1-pro[1]));
return 0;
}
work(p);
}
return 0;
}
B - AGC031E Snuke the Phantom Thief
Sol
枚举偷走的宝物的总数 K K K,然后就可以把所有的限制都转化成横坐标/纵坐标小于等于某个值 a a a的宝物的数量要小于等于/大于等于某个值 b b b,这可以等价地转化成将所有的宝物按照横坐标/纵坐标排序后,第 b + 1 / b b+1/b b+1/b个宝物的横坐标/纵坐标的范围要大于/小于等于某个值 a a a。
也就是说,对这些宝物的横坐标第 i i i大的我们限制了它的取值区间;对于纵坐标第 i i i大的我们也限制了它的取值区间。这些区间的左右端点显然会随着 i i i单调移动。
分别建两排点,一个从源点经过一些其它点连向它,表示每一个横坐标值;另一排点表示每一个纵坐标值,经过它可以到达汇点。从第一排点的 x i x_i xi向第二排点的 y i y_i yi连流量为 1 1 1费用为 v i v_i vi的边,跑最大费用最大流。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
template <class T> inline void cmin(T &x,T y) { if(y<x) x=y; }
template <class T> inline void cmax(T &x,T y) { if(x<y) x=y; }
const ll inf=1e16;
int S,T,ncnt;
namespace Flow {
const int N=410;
int head[N],cur[N];
ll dis[N];
struct ed { int to,next,f; ll w; };
vector<ed> e;
void init() { memset(head,-1,sizeof(head)),e.clear(); }
void ad(int x,int y,int f,ll w) {
e.PB((ed){y,head[x],f,w}); head[x]=e.size()-1;
e.PB((ed){x,head[y],0,-w}); head[y]=e.size()-1;
}
queue<int> que;
int vis[N];
bool bfs() {
for(int i=1;i<=ncnt;++i) dis[i]=-inf,cur[i]=head[i];
dis[S]=0,vis[S]=1,que.push(S);
while(!que.empty()) {
int u=que.front(); que.pop(),vis[u]=0;
for(int k=head[u];~k;k=e[k].next) if(e[k].f) {
int v=e[k].to;
if(dis[v]<dis[u]+e[k].w) {
dis[v]=dis[u]+e[k].w;
if(!vis[v]) vis[v]=1,que.push(v);
}
}
}
return dis[T]>-inf;
}
int dfs(int u,int f) {
if(u==T||!f) return f; int ret=0,tmp;
vis[u]=1;
for(int &k=cur[u];~k;k=e[k].next) if(e[k].f) {
int v=e[k].to;
if(!vis[v]&&dis[v]==dis[u]+e[k].w&&(tmp=dfs(v,min(f,e[k].f)))) {
e[k].f-=tmp,e[k^1].f+=tmp;
f-=tmp,ret+=tmp;
if(!f) break;
}
}
vis[u]=0;
return ret;
}
ll work() { ll ans=0; while(bfs()) ans+=dfs(S,1e9)*dis[T]; return ans; }
}
const int N=110;
const int M=410;
char ss[M],str[10];
int a[M],b[M];
int xi[N],yi[N],n,m;
int lbx[N],rbx[N],lby[N],rby[N];
int idx[N],idy[N];
ll vi[N];
ll sol(int K) {
for(int i=1;i<=K;++i)
lbx[i]=lby[i]=1,
rbx[i]=rby[i]=100;
for(int i=1;i<=m;++i) {
if(b[i]>K) continue;
if(ss[i]=='L') cmax(lbx[b[i]+1],a[i]+1);
else if(ss[i]=='R') cmin(rbx[K-b[i]],a[i]-1);
else if(ss[i]=='D') cmax(lby[b[i]+1],a[i]+1);
else cmin(rby[K-b[i]],a[i]-1);
}
for(int i=2;i<=K;++i)
cmax(lbx[i],lbx[i-1]),
cmax(lby[i],lby[i-1]);
for(int i=K-1;i>=1;--i)
cmin(rbx[i],rbx[i+1]),
cmin(rby[i],rby[i+1]);
for(int i=1;i<=K;++i) if(lbx[i]>rbx[i]||lby[i]>rby[i]) return 0;
ncnt=0; Flow::init();
S=++ncnt,T=++ncnt;
for(int i=1;i<=100;++i) idx[i]=++ncnt,idy[i]=++ncnt;
for(int i=1;i<=K;++i) {
int u=++ncnt;
Flow::ad(S,u,1,0);
for(int j=lbx[i];j<=rbx[i];++j)
Flow::ad(u,idx[j],1,0);
}
for(int i=1;i<=K;++i) {
int u=++ncnt;
Flow::ad(u,T,1,0);
for(int j=lby[i];j<=rby[i];++j)
Flow::ad(idy[j],u,1,0);
}
for(int i=1;i<=n;++i)
Flow::ad(idx[xi[i]],idy[yi[i]],1,vi[i]);
return Flow::work();
}
int main() {
rd(n);
for(int i=1;i<=n;++i) rd(xi[i]),rd(yi[i]),rd(vi[i]);
rd(m);
for(int i=1;i<=m;++i) {
scanf("%s",str);
ss[i]=str[0];
rd(a[i]),rd(b[i]);
}
ll ans=0;
for(int i=1;i<=n;++i) ans=max(ans,sol(i));
printf("%lld",ans);
return 0;
}
C - AGC028D Chords
Sol
设 f i , j f_{i,j} fi,j表示不考虑编号不在 [ i , j ] [i,j] [i,j]中的点时,所有配对方案中包含(下标最小的点是 i i i,下标最大的点是 j j j的连通块)的数量。设 g i , j g_{i,j} gi,j表示将 [ i , j ] [i,j] [i,j]区间中的点配对并且没有点连向区间以外的点的方案数。
那么有
A
n
s
=
∑
f
i
,
j
g
j
+
1
,
i
−
1
f
i
,
j
=
g
i
,
j
−
∑
k
=
i
+
1
j
−
1
f
i
,
k
g
k
+
1
,
j
Ans = \sum f_{i,j} g_{j+1,i-1} \\ f_{i,j} = g_{i,j} - \sum_{k=i+1}^{j-1} f_{i,k}g_{k+1,j}
Ans=∑fi,jgj+1,i−1fi,j=gi,j−k=i+1∑j−1fi,kgk+1,j
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=610,mod=1e9+7;
inline void Add(int &x,int y) { x+=y; if(x>=mod) x-=mod; }
inline void Dec(int &x,int y) { x-=y; if(x<0) x+=mod; }
int f[N][N],g[N][N],h[N][N],fac[N];
int A[N],B[N];
int n,m;
bool In(int x,int l,int r) { return x>=l&&x<=r; }
int main() {
rd(n),rd(m);
for(int i=1;i<=m;++i) rd(A[i]),rd(B[i]);
fac[0]=1;
for(int i=2;i<=2*n;i+=2) fac[i]=fac[i-2]*(ll)(i-1)%mod;
for(int i=1;i<=2*n;++i)
for(int j=i+1;j<=2*n;j+=2) {
int tot=0,flg=1;
for(int k=1;k<=m;++k) {
flg&=In(A[k],i,j)==In(B[k],i,j);
tot+=In(A[k],i,j)+In(B[k],i,j);
}
if(!flg) continue;
g[i][j]=fac[j-i+1-tot];
h[i][j]=fac[2*n-m*2-(j-i+1-tot)];
}
int ans=0;
for(int len=2;len<=2*n;len+=2)
for(int i=1;i+len-1<=2*n;++i) {
int j=i+len-1;
f[i][j]=g[i][j];
for(int k=i+1;k<j;k+=2)
Dec(f[i][j],f[i][k]*(ll)g[k+1][j]%mod);
Add(ans,f[i][j]*(ll)h[i][j]%mod);
}
printf("%d",ans);
return 0;
}
j-i+1-tot)];
}
int ans=0;
for(int len=2;len<=2*n;len+=2)
for(int i=1;i+len-1<=2*n;++i) {
int j=i+len-1;
f[i][j]=g[i][j];
for(int k=i+1;k<j;k+=2)
Dec(f[i][j],f[i][k]*(ll)g[k+1][j]%mod);
Add(ans,f[i][j]*(ll)h[i][j]%mod);
}
printf("%d",ans);
return 0;
}