打开题面:
看到t3的题目,点进去看了看,很好,再见,点进去t1看了看,是个状压,想了一下,应该是三进制,想优化,不会,于是想dfs,看能不能剪枝,先走了,看了看t2,嗯,是个dp,突然发现今天三道都是概率,瑟瑟发抖,于是不想写,……,于是被迫写,看了看t3,gauss一波应该也行,于是回去看最有可能做出来的t2,话说t2我见过,但是没写,就是因为记得不是在写概率的时候见的,所以才敢看,想了一个dp,想着把vi固定,求出di,想了个o(n)的dp,感觉还行,开始写,写完开始调,调到11点过了样例,一交全WA,不慌,又是磕几个样例,没发现有问题啊???又试了long long 也没问题,不会是我dp想错了把,感觉没问题啊??? 但是11点多了,赶紧去写t1,dfs写完了,一稿样例,过了,但是突然反应过来,这样做没有消除非独立集点的影响,12点多了,好慌啊,赶紧打了个10分的暴力,连10分的暴力都打的磕磕巴巴,写了2、30分钟,踩着点交的。
总结:
看到题了不要慌,慌了也不要不写,就算难也不是拿不到分,至少要把最暴力的拿了。
看到像什么算法,也不要局限在类似算法上,想想类似的其他题我是怎么做的,也不要想非常nb的算法,像非常简单的算法差分啊,模拟啊,都有可能拿到分,可能会有不一样的收获
现在又要加上一条了,一定要看清树的形态,二叉、多叉、链
题解:
搞事的时候没有想到也正常,从来主动没有这样想过或者运用过这种扩展的思想
https://www.luogu.com.cn/blog/user29936/solution-p5492
代码:
#include<bits/stdc++.h> using namespace std; const int N=22,p=998244353; typedef long long ll; int siz[(1<<20)+5],n,g[N][N],f[(1<<20)+5],ones[(1<<20)+5],one[(1<<20)+5]; //siz 是集合 s[i]=ones[siz[i]]; vector<int> S[N]; int get(int s) { int cnt=0; while(s) cnt++,s-=s&-s; return cnt; } int inv[N]; void init() { inv[0]=inv[1]=1; for(int i=2;i<=n;i++) inv[i]=-1ll*p/i*inv[p%i]%p; } bool b[(1<<20)+5],is[(1<<20)+5]; int main() { freopen("algorithm.in","r",stdin); freopen("algorithm.out","w",stdout); int m; cin>>n>>m; init(); int x,y; while(m--) { cin>>x>>y; g[x][y]=g[y][x]=1; } for(int i=0;i<(1<<n);i++) ones[i]=get(i),S[ones[i]].push_back(i); for(int i=0;i<n;i++) one[1<<i]=i+1; //每次只扩展一个点 siz[0]=0; is[0]=1; //判断独立集 for(int s=1;s<(1<<n);s++) { int t=s&-s; if(!is[s^t]) continue; siz[s]=siz[s^t]; is[s]=1; int k=t; t=one[t]; for(int j=1;j<=n;j++) if(t^j&&s>>(j-1)&1&&g[t][j]) is[s]=0; if(!is[s]) continue; for(int j=1;j<=n;j++) if(g[j][t]) k|=(1<<(j-1)); siz[s]|=k; } f[0]=1; b[0]=1; for(int s=1;s<(1<<n);s++) { if(!is[s]) continue; for(int i=1;i<=n;i++) if(s>>(i-1)&1) f[s]=(f[s]+1ll*f[s^(1<<(i-1))]*inv[n-ones[siz[s^(1<<(i-1))]]]%p)%p,b[s]|=b[s^(1<<(i-1))]; } for(int i=n;i>=1;i--) { int ans=0,fl=0; for(int j=0;j<S[i].size();j++) { ans=(ans+f[S[i][j]])%p; fl|=b[S[i][j]]; } if(fl) { cout<<(ans+p)%p; break; } } return 0; }
t2:
话说也不知道为什么40分不对??
但是正解的dp和我的不一样
40分:
把叶子的val排序,变成v1,v2,v3……
f[x][i] 在x位置处出现v[i]的概率,l是左孩子,r是右孩子
f[x][i] = px * ( f[l][i ]* Σ f[r][k] + f[r][i] * Σ f[l][k] ) (k=1,2,……i-1) + (1-px) * (f[l][i ]* Σ f[r][k] + f[r][i] * Σ f[l][k]) (k=i+1,i+2,……cnt_v)
前缀和优化一下就行了
正解套一个线段树合并