其他分享
首页 > 其他分享> > P5856「SWTR-03」Game

P5856「SWTR-03」Game

作者:互联网

题意

我自闭了,连蓝题都不会了,还得看题解。

以下是我理解的官方做法,献给给广大没看懂官方题解的神仙们。作者蒟蒻,如果有什么不对的地方请指出。

观察题目的限制,发现\(q\)是一个\(p^z\)的形式,因此我们可以考虑每个质数\(p\)。

对于每个质数\(p\),我们求出一个\(0-1\)串\(state_i\),其中第\(i\)位为\(1\)则表示存在一个数,它分解后\(p\)这个质因子的次幂为\(i\)。特殊地,如果一个数不含\(p\)这个质因子,那么第\(0\)位为\(1\)。\(p\)的次幂的上界很小,\(p=2\)时最大,才\(\log_2\)级别,因此是开得下的。

现在我们考虑通过题中的操作使得\(a_{1...n}\)相同的过程:
对于每个质数\(p\),显然只有所有\(a_i\)分解后的\(p\)的次幂相同才会满足条件。

先假设我们要将所有的\(a_i\)中\(p\)的次幂都消成\(0\)。

此时,我们要对\(p\)的状态求出一个最小的集合,满足将这个集合内的数任意相加可以拼出\(p\)的状态中所有的位置。

就拿官方题解里的例子吧:
对于\(1000111010\),在\(1,3,4,5,9\)位上是\(1\)(注意我们是从右向左从零开始数的),于是\(f_{(1000111010)_2}=3\),因为我们可以通过\(\{1,3,5\}\)拼出所有的数。(\(1=1\),\(3=3\),\(4=1+3\),\(5=5\),\(9=1+3+5\))

此时我们在\(q=p^1\)时操作分解后\(p\)的次幂为\(1,4,9\)的位置,在\(q=p^3\)时操作分解后\(p\)的次幂为\(3,4,9\)的位置,在\(q=p^5\)时操作分解后\(p\)的次幂为\(5,9\)的位置,那么所有的数中\(p\)的次幂都为\(0\)。

那么我们就设\(f_s\)表示状态\(s\)的答案,我们可以\(dfs\)求出。

但是包含\(s\)的状态\(t\)(即\(s\)是\(t\)的子集)的答案\(f_t\)也可以更新\(f_s\)(能拼出\(t\)就能拼出\(s\)),我们再枚举每个状态,更新它的子集即可。

现在考虑我们不把所有的\(a_i\)中的\(p\)的次幂消成\(0\)的情况。

这时候需要满足该状态最低位(即第\(0\)位)不为\(0\),因为为\(0\)的话就说明有一个数根本就没有\(p\)这个质因子,那肯定要全消完。

那么我们保留\(p^k\)就相当于\(s_p>>k\)这个状态的答案,还是拿之前的那个举例:
\(1000111010\),在\(1,3,4,5,9\)位上是\(1\)。
我们现在保留\(p^1\),也就是我们要找最小的集合,使其能拼成\(0,2,3,4,8\),那么对应的状态就是\(s_p>>1\)。

由衷地感叹:这题的确是道思维好题。

code(真·抄的官方题解):

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const int maxm=20;
int n,ans;
int a[maxn],f[1<<maxm],state[1<<maxm],cnt[1<<maxm];
bool vis[maxn];
vector<int>prime;
inline void pre_work()
{
    vis[1]=1;
    for(int i=2;i<=1000000;i++)
    {
        if(!vis[i])prime.push_back(i);
        for(unsigned int j=0;j<prime.size()&&i*prime[j]<=1000000;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
void dfs(int dep,int now,int s)
{
    f[s]=min(f[s],dep-1);
    if(dep>5)return;
    for(int i=now;i<=20;i++)dfs(dep+1,i,(s|(s<<i))&((1<<20)-1));
}
int main()
{
    pre_work();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    memset(f,0x3f,sizeof(f));
    dfs(1,1,1);
    for(int s=(1<<20)-1;s;s--)
        for(int j=1;j<=20;j++)
            if(!((s>>(j-1))&1))f[s]=min(f[s],f[s|(1<<(j-1))]);
    for(int s=1;s<(1<<20);s++)if(!(s&1))f[s]=min(f[s],f[s>>1]);
    for(int i=1;i<=n;i++)
    {
        int tmp=a[i];
        for(unsigned int j=0;j<prime.size()&&prime[j]*prime[j]<=tmp;j++)
        {
            if(tmp%prime[j])continue;
            int k=0;
            while(tmp%prime[j]==0)k++,tmp/=prime[j];
            state[prime[j]]|=1<<k;cnt[prime[j]]++;
        }
        if(tmp>1)cnt[tmp]++,state[tmp]|=2;
    }
    for(int i=2;i<=1000000;i++)
    {
        if(cnt[i]!=n)state[i]|=1;
        ans+=f[state[i]];
    }
    printf("%d",ans);
    return 0;
}

标签:03,P5856,状态,int,题解,质数,拼出,Game,我们
来源: https://www.cnblogs.com/nofind/p/12130830.html