1. 程式人生 > 其它 >2021牛客暑期多校訓練營9

2021牛客暑期多校訓練營9

比賽連結: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\n",a*n%md); return 0; }
View Code

J

分析:

首先,右轉不會影響任何路線,也不會被任何路線影響,所以單獨考慮,最後算答案時比較一下即可。

剩下的就是左轉和直行。觀察一番後可以發現,最多有兩個燈同時亮起,而且只有四種情況:直行+對面直行,左轉+對面左轉,直行+左邊左轉,直行+自己左轉。

換句話說,每個直行有三種燈可以帶:前直,左左,自左;每個左轉有三種燈可以帶:前左,右直,自直。

所以現在問題可以轉化成:預設每個燈單獨亮,然後考慮如何安排使得可以相互帶的燈實現最大匹配。

可以拆點,然後二分圖匹配——由於還有車流量各種,所以網路流跑一個最大流即可。拆點後會有重複的情況,但是都是對稱的,所以直接最大流除以二。

複習了一下網路流的dinic演算法,看了兩篇

程式碼如下:

#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)\n",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\n",max(lf-flow/2,right));
    }
    return 0;
}
View Code