SRM 548

A
二分加贪心
code:


class KingdomAndTrees {
    public:
    int n;
    bool judge(const int x,vector<int>heights)
    {
        int las=0;
        for(int i=0;i<n;i++)
        {
            int now=heights[i];
            if(now<=las)
            {
                if(now+x<=las) return false;
                las++;
            }
            else las=max(now-x,las+1);
        }
        return true;
    }
    int minLevel(vector<int> heights) {
        n=heights.size();
        int l=0,r=inf;
        while(l<=r)
        {
            int mid=((ll)l+r)>>1;
            if(judge(mid,heights)) r=mid-1;
            else l=mid+1;
        }
        return r+1;
    }
};

B
算出g[i]表示剩余能用的数中,比b中的i个数大的有多少个,用zero表示a中0的数量
获胜概率 p=sum/(nn) sum=ni=1nj=1[ai>bj] 最小化(p-0.5)
f[i][j]表示前i个g[i],使得sum=j,至少用多少个0,dp,得出哪些sum是可行的
code:

const long double eps = 1e-10;

class KingdomAndDice {
    public:
    int n,al;
    int a[maxn],b[maxn],c[maxn];
    int f[110][maxn];
    inline void down(int &x,const int &y){if(x==-1||x>y)x=y;}
    double newFairness(vector<int> firstDie, vector<int> secondDie, int X) {
        n=firstDie.size();
        for(int i=0;i<n;i++) a[i]=firstDie[i],b[i]=secondDie[i];
        sort(a,a+n); sort(b,b+n);

        al=n*n;
        int now=0,zero=0;
        for(int i=0;i<n;i++) if(!a[i]) zero++;
        for(int i=0;i<n;i++) if(a[i]) 
            for(int j=0;j<n;j++) if(a[i]>b[j]) now++;

        b[n]=X+1; c[0]=zero;
        for(int i=0;i<n;i++)
        {
            c[i+1]=b[i+1]-b[i]-1;
            for(int j=0;j<n;j++) if(b[i]<a[j]&&a[j]<b[i+1]) c[i+1]--;
        }
        memset(f,-1,sizeof f); f[0][now]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=al;j++) if(f[i-1][j]!=-1)
                for(int k=0;k<=c[i]&&j+i*k<=al;k++) down(f[i][j+i*k],f[i-1][j]+k);
        }
        int re=al; double p=(double)re/al;
        for(int i=al;i>=0;i--) if(f[n][i]!=-1&&f[n][i]<=zero)
            if(fabs((double)i/al-0.5)-fabs(p-0.5)<=eps) re=i,p=(double)re/al;
        return p;
    }
};

C
比较恶心的讨论,细节挺多的
求n个点m条边编号前K个点度数为2的联通无向图有多少种
用E[i]=i*(i-1)/2表示i个点的完全图边数,f[i][j]表示i个点j条边的无向连通图数量,用所有情况减不连通的数量算得f
于是K=0的情况就是f[n][m]
当K=1时,从原图中去掉这个点,可能剩下1或2个联通块,枚举点n所在联通块计算
当K=2时,若这两个点之间有边,大致与K=1时相同,若无边,去掉这两个点,可能剩下1/2/3个联通块,同样枚举点n的联通块,与点n不同联通块的最大编号点u联通块,计算答案

code:

#define ll long long
#define maxn 55
#define maxm 1300
#define __ %=Mod

using namespace std;

const ll Mod = 1e9+7;

class KingdomAndCities {
    public:
    int E[maxn];
    ll C[maxm][maxm],f[maxn][maxm];
    void pre()
    {
        for(int i=1;i<maxn;i++) E[i]=i*(i-1)>>1;
        int u=E[50];
        for(int i=0;i<=u;i++)
        {
            C[i][0]=1ll;
            for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod;
        }
        f[1][0]=1ll;
        for(int i=2;i<=50;i++) for(int j=i-1,uj=min(E[i],50);j<=uj;j++)
        {
            f[i][j]=C[E[i]][j];
            for(int a=1;a<i;a++) for(int b=a-1,ub=min(j,E[a]);b<=ub;b++)
                (f[i][j]-=C[i-1][a-1]*f[a][b]%Mod*C[E[i-a]][j-b]%Mod)__;
            (f[i][j]+=Mod)__;
        }
    }
    int howMany(int N, int M, int K) {
        pre();
        if(!M) return (int)f[N][K];
        ll re=0;
        N-=M;
        if(M==1)
        {
            if(N<=1||K<2) return (int)re;
            K-=2;
            re=f[N][K]*C[N][2]%Mod;
            for(int a=1;a<N;a++)
            {
                for(int b=a-1,ub=min(E[a],K);b<=ub;b++)
                    (re+=f[a][b]*C[N-1][a-1]%Mod*a%Mod*f[N-a][K-b]%Mod*(N-a)%Mod)__;
            }
        }
        if(M==2)
        {
            if(N<=0||K<3) return (int)re;
            K-=3;
            re=f[N][K]*N%Mod*N%Mod;
            for(int a=1;a<N;a++)
            {
                for(int b=a-1,ub=min(E[a],K);b<=ub;b++)
                    (re+=C[N-1][a-1]*f[a][b]%Mod*f[N-a][K-b]%Mod*a%Mod*(N-a)*2ll%Mod)__;
            }
            K--; if(K<0) return (int)re;
            (re+=f[N][K]*(C[N][2]*C[N][2]%Mod)%Mod)__;
            for(int a=1;a<N;a++)
            {
                for(int b=a-1,ub=min(E[a],K);b<=ub;b++)
                {
                    ll temp;
                    temp=C[N-1][a-1]*f[a][b]%Mod*f[N-a][K-b]%Mod;
                    (re+=temp*a*(N-a)%Mod*a*(N-a)%Mod)__;
                    (re+=2ll*temp%Mod*((ll)a*(N-a)*C[N-a][2]+(ll)(N-a)*a*C[a][2])%Mod)__;
                }
            }
            ll t3=0;
            for(int A=1;A<=N-2;A++) for(int B=1;A+B<N;B++)
            {
                int c=N-A-B;
                ll tmp=C[N-1][A-1]*C[N-A-1][B-1]%Mod*(2ll*A*B*c*(A+B+c)%Mod)%Mod;
                for(int a=A-1,ua=min(E[A],K);a<=ua;a++) for(int b=B-1,ub=min(E[B],K-a);b<=ub;b++)
                    (t3+=tmp*f[A][a]%Mod*f[B][b]%Mod*f[c][K-a-b]%Mod)__;
            }
            (re+=t3)__;
        }
        return (int)re;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值