SCUT - 254 - 欧洲爆破 - 概率dp - 状压dp
思路很清晰,写起来很恶心。
#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)); } }