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

 

posted @ 2021-08-23 14:32  Zinn  阅读(55)  评论(0编辑  收藏  举报