5.9总结

打开题面:

看到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)

前缀和优化一下就行了

 正解套一个线段树合并

原文地址:https://www.cnblogs.com/xsm098/p/14748132.html