码迷,mamicode.com
首页 > 其他好文 > 详细

SCUT - 254 - 欧洲爆破 - 概率dp - 状压dp

时间:2019-03-28 13:37:53      阅读:168      评论:0      收藏:0      [点我收藏+]

标签:int   思路   std   状压dp   return   http   输入   ++   cut   

https://scut.online/p/254

思路很清晰,写起来很恶心。

#include<bits/stdc++.h>
using namespace std;
#define ll long long

int dp[1<<20];
//dp[k] 从状态k开始直到k=0还需要的期望次数 0表示炸弹已爆炸,1表示炸弹未爆炸
int x[20];
int y[20];
int r[20];

int pa[20];

ll n;
ll invn;
int all1;

int vis[20];

bool canboom(int id,int id2){
    return (1ll*r[id]*r[id])>=(1ll*(x[id]-x[id2])*(x[id]-x[id2])+1ll*(y[id]-y[id2])*(y[id]-y[id2]));
}

void dfs(int id){
    vis[id]=1;
    for(int i=0;i<n;i++){
        if(vis[i]==0&&(canboom(id,i))){
            dfs(i);
        }
    }
}

void find_pa(int id){
    memset(vis,0,sizeof(vis));

    dfs(id);
    //把自己引爆;

    //cerr<<"boom:"<<id<<" can boom: \n";
    /*for(int i=0;i<n;i++){
        cerr<<vis[i];
    }
    cout<<endl;*/

    int ans=all1;
    //假设所有炸弹都能被引爆
    for(int i=0;i<n;i++){
        if(vis[i]){
            //从左往右的第i个不能被引爆
            //0 1 2 3
            //二进制的低位都是在右边
            //3 2 1 0
            ans&=(~(1<<i));
            //&=1110111设为0,该炸弹不能被引爆
            //cout<<bitset<3>(~(1<<(n-1-i)))<<endl;
        }

    }
    //cout<<bitset<3>(ans)<</*"!"<<*/endl;
    pa[id]=ans;
}

int p=1000000009;

//快速幂 x^n%p
ll qpow(ll x,ll n) {
    ll res=1;
    while(n) {
        if(n&1)
            res=res*x%p;
        x=x*x%p;
        n>>=1;
    }
    return res%p;
    //要记得模p,否则输入一个2的幂次模1就挂了
}


//乘法逆元 快速幂+费马小定理 模必须是质数
ll inv(ll n) {
    return qpow(n,p-2);
}

int count0(int k){
    int cnt=0;
    for(int i=0;i<n;i++){
        if(!(k&1))
            cnt++;
        k>>=1;
    }
    return cnt;
}

int fdp(int k){
    //cout<<"[fdp]="<<" "<<bitset<3>(k)<<endl;
    if(dp[k]!=-1){
        //cerr<<" dp["<<k<<"]="<<dp[k]<<endl;
        return dp[k];
    }

    int cnt0=count0(k);

    //cout<<"k="<<k<<" cnt0="<<cnt0<<endl;
    ll ans=0;

    for(int i=0;i<n;i++){
        if(k&(1<<i)){
            //cout<<"i="<<i<<endl;
            //k的右起第i位,第i个炸弹没被引爆
            int can=pa[i];
            //第i个炸弹把can为1的这些都引爆,也就是把can为1的设成0
            //cout<<"can="<< bitset<3>(can)<<endl;
            ans+=(fdp(k&(can)))*invn;
            //cerr<<" tmp ans="<<ans<<endl;
            ans%=p;
        }
    }

    ans+=1;
    ans%=p;

    //cerr<<" tmp dp["<<k<<"]="<<ans<<endl;

    ans*=n;
    ans%=p;

    ans*=inv(n-cnt0);
    ans%=p;
    //cerr<<" dp["<<k<<"]="<<ans<<endl;
    return dp[k]=ans;
}

int main(){
    //cout<<"inv2="<<inv(2)<<endl;
    while(~scanf("%lld",&n)){
        invn=inv(n);

        for(int i=0;i<n;i++){
            scanf("%d%d%d",x+i,y+i,r+i);
        }

        all1=0;
        for(int i=0;i<n;i++){
            all1<<=1;
            all1|=1;
        }

        for(int i=0;i<n;i++){
            //cerr<<"!";
            find_pa(i);
        }

        memset(dp,-1,sizeof(dp));

        dp[0]=0;//所有炸弹都爆炸了

        //求dp[all1]

        //cout<<bitset<3>(all1)<<endl;

        printf("%d\n",fdp(all1));
    }
}

 

SCUT - 254 - 欧洲爆破 - 概率dp - 状压dp

标签:int   思路   std   状压dp   return   http   输入   ++   cut   

原文地址:https://www.cnblogs.com/Yinku/p/10614324.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!