比赛链接:https://ac.nowcoder.com/acm/contest/11260
E,H,J,10。不错。
I
分析:
题目意思是,(A)和(B)抢夺(n)块地盘,一开始(A)的能力值是(a),(B)的能力值是(b);每抢上一块地盘,抢到的人的能力值会增加(w)。(A)抢上的概率是( frac{A的能力值}{当前总能力值} ),(B)同。问最终(A)抢到地盘数的期望。
于是可以设(E(x))表示经过(x)轮后(A)的期望能力值。有转移方程:
( E(x+1) = E(x) + frac{E(x)}{1+wx}*w )
移项可以得到:
( frac{E(x+1)}{1+w(x+1)} = frac{E(x)}{1+wx} = E(0) = a )
然后答案可以通过最终的能力值得到,因为能力值和抢几块地盘直接相关:
( ans = frac{E(n)-E(0)}{w} = a*n )
代码如下:
#include<iostream> #define ll long long using namespace std; int const md=998244353; int n,w,x,y; ll pw(int a,int b) { ll ret=1,na=a; while(b) { if(b&1)ret=(ret*na)%md; na=(na*na)%md; b>>=1; } return ret; } int main() { scanf("%d%d%d%d",&n,&w,&x,&y); ll a=(x*pw(y,md-2))%md; printf("%lld ",a*n%md); return 0; }
J
分析:
首先,右转不会影响任何路线,也不会被任何路线影响,所以单独考虑,最后算答案时比较一下即可。
剩下的就是左转和直行。观察一番后可以发现,最多有两个灯同时亮起,而且只有四种情况:直行+对面直行,左转+对面左转,直行+左边左转,直行+自己左转。
换句话说,每个直行有三种灯可以带:前直,左左,自左;每个左转有三种灯可以带:前左,右直,自直。
所以现在问题可以转化成:默认每个灯单独亮,然后考虑如何安排使得可以相互带的灯实现最大匹配。
可以拆点,然后二分图匹配——由于还有车流量各种,所以网络流跑一个最大流即可。拆点后会有重复的情况,但是都是对称的,所以直接最大流除以二。
代码如下:
#include<iostream> #include<cstring> #include<queue> using namespace std; int const N=20,M=200,inf=100000; int T,n,a[N][N],hd[N],nxt[M],cnt,to[M],w[M],num[N],ed=17; int d[N],cur[N]; queue<int>q; int md(int x){if(x>8)x-=8; return x;} int md2(int x){if(x<=0)x+=8; return x;} void add(int x,int y,int f) { //printf("add(%d,%d) ",x,y); nxt[++cnt]=hd[x]; hd[x]=cnt; to[cnt]=y; w[cnt]=f; nxt[++cnt]=hd[y]; hd[y]=cnt; to[cnt]=x; w[cnt]=0; } bool bfs() { while(q.size())q.pop(); memset(d,0,sizeof d); q.push(0); d[0]=1; while(q.size()) { int u=q.front(); q.pop(); for(int i=hd[u],v;i>-1;i=nxt[i]) if(!d[v=to[i]]&&w[i])d[v]=d[u]+1,q.push(v); } return d[ed]; } int dfs(int u,int f) { if(u==ed)return f; int res=0; for(int &i=cur[u],v;i>-1;i=nxt[i]) if(d[v=to[i]]==d[u]+1&&w[i]) { int k=dfs(v,min(w[i],f-res)); res+=k; w[i]-=k; w[i^1]+=k; if(res==f)return f; } if(!res)d[u]=0; return res; } int main() { scanf("%d",&T); while(T--) { int lf=0; for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) scanf("%d",&a[i][j]),lf+=a[i][j]; int right=max(a[1][4],max(a[2][1],max(a[3][2],a[4][3]))); lf-=(a[1][4]+a[2][1]+a[3][2]+a[4][3]); num[1]=a[1][3]; num[2]=a[1][2]; num[3]=a[2][4]; num[4]=a[2][3]; num[5]=a[3][1]; num[6]=a[3][4]; num[7]=a[4][2]; num[8]=a[4][1]; cnt=-1; memset(hd,-1,sizeof hd); for(int i=1;i<=8;i++)add(0,i,num[i]),add(i+8,ed,num[i]); for(int i=1;i<=8;i++) { if(i%2) add(i,md(i+4)+8,inf),add(i,md(i+3)+8,inf),add(i,md(i+1)+8,inf);//直:前直,左左,自左 else add(i,md(i+4)+8,inf),add(i,md2(i-3)+8,inf),add(i,md2(i-1)+8,inf);//左:前左,右直,自直 } int flow=0; while(bfs()) { memcpy(cur,hd,sizeof hd); flow+=dfs(0,inf); } printf("%d ",max(lf-flow/2,right)); } return 0; }