zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Infopath Notify 弹出提示信息
    window.showModalDialog 返回值
    【转】获得正文内容中的所有img标签的图片路径
    Json Datable Convert
    Sharepoint 列表 附件 小功能
    Surgey 权限更改
    SQL 触发器用于IP记录转换
    Caml语句 查询分配给当前用户及当前组
    jquery 1.3.2 auto referenced when new web application in VSTS2010(DEV10)
    TFS diff/merge configuration
  • 原文地址:https://www.cnblogs.com/nofind/p/12130830.html
Copyright © 2011-2022 走看看