Divide by Zero 2017 and Codeforces Round #399 (Div. 1 + Div. 2, combined)

这把比赛题目不错,然而打得太挫了。。来写一发题解

C. Jon Snow and his Favourite Number

基本上是暴力模拟,不过排序和异或要写 O(1000) 的。

#include <bits/stdc++.h>


using namespace std;

const int maxn = 100010;
int n,k,x;

int cnt[1024][2];

int main(){

    cin>>n>>k>>x;

    for(int i=0;i<n;i++){
        int num;
        scanf("%d",&num);
        cnt[num][0]++;
    }

    int cur = 0;
    int pre = 1;
    while(k--){
        swap(cur,pre);
        //sort

        for(int i=0;i<1024;i++){
            cnt[i][cur] = 0;
        }

        int p = 0;
        for(int i=0;i<1024;i++){
            cnt[i][cur] += cnt[i][pre]>>1;
            cnt[i^x][cur] += cnt[i][pre]>>1;

            if((cnt[i][pre])&1){
                if(p&1){
                    cnt[i][cur]++;
                }else{
                    cnt[i^x][cur]++;
                }
            }
            p+=cnt[i][pre];
        }


    }
    for(int i=1023;;i--){
        if(cnt[i][cur]){
            cout<<i<<" ";
            break;
        }
    }

    for(int i=0;;i++){
        if(cnt[i][cur]){
            cout<<i<<endl;
            break;
        }
    }
    return 0;
}

D. Jon and Orbs

考虑到每个orb是等价的,状态用当前已有多少个orb来表示。递推计算出来每天有1~ k 个orb的概率(题目要求的最大概率是1/2,递推到第100000天绝对够了),处理好p取1~1000的答案,再回答。

#include <bits/stdc++.h>


using namespace std;

double p_count[1010];

int ans[1010];

int main(){
    int k,q;
    cin>>k>>q;
    int p = 1;
    double target;
    for(int i=0;i<=k;i++){
        p_count[i] = 0;
    }

    p_count[0] = 1.0;
    for(int day = 1;p<=1000;day++){
        target = (p+0.0)/2000;
        for(int i=k;i>0;i--){
            double tmp = p_count[i-1] * ((k-i+1.0)/k);
            p_count[i] += tmp;
            p_count[i-1] -= tmp;
        }

        while(p_count[k] >= target){
            ans[p] = day;
            p++;
            target = (p+0.0)/2000;
            if(p>1000)break;
        }
    }


    while(q--){
        cin>>p;
        cout<<ans[p]<<endl;
    }
    return 0;
}

E. Game of Stones

很有意思的一个题。解题的关键是注意到一堆数量为1的石子和一堆数量为2的石子是等效的。因为这两种堆都只能够被拿一次。仔细分析我们会发现,石子的数量在1以上,相当于传统nim的1,石子的数量在1+2=3以上,相当于传统nim的2,石子的数量在1+2+3=6以上,相当于传统nim的3…把每堆数量转化一下,就是变成一个传统的nim博弈问题了。另外每堆上限仅60,据说可以搜索。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

int cnt[66];

int a[66];

int main(){
    int n; 
    cin>>n;

    for(int i=0;i<n;i++){
        int num;
        scanf("%d",&num);
        cnt[num]++;
    }

    for(int i=1;i<=60;i++){
        a[i] = a[i-1] + i;
    }

    vector<int> p;
    for(int i=1;i<=60;i++){
        cnt[i] %= 2;
    }

    for(int i=1;i<=60;i++){
        if(cnt[i]){
            for(int j=1;j<=60;j++){
                if(a[j]>i){
                    cnt[j-1]++;
                    cnt[i]--;
                    break;
                }
            }
        }
    }

    int sum = 0;
    for(int i=1;i<=60;i++){
        while(cnt[i]--){
            sum ^= i;
        }
    }

    if(sum){
        puts("NO");
    }else{
        puts("YES");
    }

    return 0;
}

F. Barrels and boxes

组合数学问题。先说分母(总数),枚举酒的堆数 i ,由组合数学知识,相当于w个小球中间的 w1 个位置放 i1 个隔板,也就是求一个组合数,此时食物的堆数只能是 i1 ~ i+1 ,计算方法同酒,然后把它们乘起来。
分子(符合条件的情形数),也是枚举酒的堆数 i ,因为有高度要求,把酒的数量减去ih,再进行与分母一样的运算即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const ll mod = 1e9 + 7;

ll fac[100010];

//扩展欧几里德   
void ExEuclid(ll a,ll b,ll &x,ll &y,ll &q){  
    if(b==0){  
        x=1;y=0;q=a;  
        return;  
    }  
    ExEuclid(b,a%b,y,x,q);  
    y-=x*(a/b);  
}  

//逆元   
ll inv(ll num){  
    ll x,y,q;  
    ExEuclid(num,mod,x,y,q);  
    if(q==1)return (x+mod)%mod;  
}  

ll C(int n,int k){
    if(n<0 || k<0)return 0;
    if(n<k)return 0;
    ll res = fac[n];
    res *= inv(fac[k]);
    res %= mod;
    res *= inv(fac[n-k]);
    res %= mod;
    return res;
}

ll solve(int f,int w,int mn,int mx){
    if(w == 0 || f == 0)return 1;

    ll res = 0;
    // w -> i
    for(int i=mn;i<=mx;i++){
        ll t = C(w-1,i-1);
        if(i>1){
            res += t * C(f-1,i-2);
        }
        res += t * C(f-1,i-1) * 2;
        res += t * C(f-1,i);
        res %= mod;
    }
    res %= mod;
    return res;
}

int main(){
    fac[0] = 1;
    for(int i=1;i<=100000;i++){
        fac[i] = fac[i-1] * i;
        fac[i] %= mod;
    }

    int f,w,h;
    cin>>f>>w>>h;

    //p/q
    ll p = 0;
    ll q = 0;
    if( h == 0){
        p = solve(f,w,1,w);
        q = solve(f,w,1,w);
    }else{
        q = solve(f,w,1,w);

        for(int i=1;;i++){
            int ww = w - i*h;
            if(ww<i)break;
            p += solve(f,ww,i,i);
        }
        p %= mod;
        if(w == 0){
            p = 1;
            q = 1;
        }
    }

    ll ans = p * inv(q);
    ans %= mod;
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值