9.28 csp-s模拟测试54 x+y+z

Posted jrf123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.28 csp-s模拟测试54 x+y+z相关的知识,希望对你有一定的参考价值。

T1 x

求出每个数的质因数,并查集维护因子相同的数,最后看一共有多少个联通块,$ans=2^cnt-2$

但是直接分解会$T$,埃筛是个很好的选择,或者利用每个数最多只会有1个大于$\sqrtn$的质因子,线筛$1e6$内的素数,每次只需枚举$1e3$的质因数就行,复杂度也可以过去

技术图片
#include<iostream>
#include<cstdio>
#include<bitset>
#include<cmath>
#include<cstring>
#include<vector>
#define ll long long
#define mod 1000000007
using namespace std;
ll T,n,ans,a[100100],prime[100100],fa[100100],num,pr[1001000],tot;
bitset<100100>vis;
bitset<1001000>v;
vector<int>ve[1001000];
ll read()

    ll aa=0,bb=1;char cc=getchar();
    while(cc>9||cc<0)if(cc==-) bb=-1;cc=getchar();
    while(cc>=0&&cc<=9)aa=(aa<<3)+(aa<<1)+(cc^0);cc=getchar();
    return aa*bb;

ll quick(ll x,ll p)

    ll as=1;
    while(p)
        if(p&1) as=as*x%mod;
        x=x*x%mod;
        p>>=1;
    
    return as;

ll find(ll x)

    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];

void init()

    for(int i=2;i<=1000000;i++)
        if(v[i]) continue;prime[++tot]=i;
        for(int j=i;j<=1000000;j+=i)
            v[j]=1;
            ve[j].push_back(i);
        
    

int main()

    T=read();init();
    while(T--)
        n=read();ans=0;num=0;vis.reset();memset(pr,0,sizeof(pr));
        for(int i=1;i<=n;i++) a[i]=read(),fa[i]=i;
        for(int i=1;i<=n;i++)
            for(int j=0;j<ve[a[i]].size();j++)
                if(pr[ve[a[i]][j]]) fa[find(i)]=find(pr[ve[a[i]][j]]);
                else pr[ve[a[i]][j]]=i;
            
        
        for(int i=1;i<=n;i++)
            int f=find(i);
            if(!vis[f]) vis[f]=1,num++;
        
        ans=(quick(2,num)-2+mod)%mod;
        printf("%lld\n",ans);
    
    return 0;
x

 

 

T2 y

$bitset$的灵活应用

一开始的思路是$2^20$枚举状态,记忆化搜索,$f[i][sta]$表示从i点出发能否走出$sta$的状态(状态的第一位表示长度),但是空间开不下,时间也扛不住

所以改变策略,只记录一半的状态,最后枚举中间点

$f[i][sta][j]$表示起点为$i$,终点为$j$,中间为状态$sta$是否可行

正常需要枚举一个点,一个状态,然后在枚举一个点,再枚举与第二个点有边相连的点,如果前两个点之间的$sta$状态可行,那么第一个点与第三个点之间$sta<<1|h[i].w$也是可行的

但是,时间显然不优秀

$bitset$就有用了,如果两个点之间的$sta$状态是可行的,那么第一个点与这个状态的终点和第二个点的连边是一样的,所以建边的时候用$bitset$邻接表,再加一维表示是$0$还是$1$,$bitset$合并就行,最后只需要枚举状态和中点,看是否能拼成这个状态

还有就是要在每个状态的第一位表示长度,不然$01$和$001$状态是分不清的。对于$01$串为奇数的要处理好$len/2$与$len/2+1$两个长度的关系

技术图片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<bitset>
using namespace std;
int n,m,d,ans;
bitset<99>f[99][(1<<12)+5],bt[2][99];
int read()

    int aa=0,bb=1;char cc=getchar();
    while(cc>9||cc<0)if(cc==-) bb=-1;cc=getchar();
    while(cc>=0&&cc<=9)aa=(aa<<3)+(aa<<1)+(cc^0);cc=getchar();
    return aa*bb;

int main()

    n=read();m=read();d=read();int dd=d/2,dis=d-dd;
    int u,v,c;
    for(int i=1;i<=m;i++)
        u=read();v=read();c=read();
        bt[c][u][v]=1;bt[c][v][u]=1;
        f[u][c|2][v]=1;f[v][c|2][u]=1;
    
    for(int i=1;i<=n;i++)
        for(int sta=2;sta<(1<<(dis+1));sta++)
            for(int j=1;j<=n;j++)
                if(!f[i][sta][j]) continue;
                f[i][sta<<1|1]|=bt[1][j];
                f[i][sta<<1|0]|=bt[0][j];
            
        
    
    for(int i=0;i<(1<<d);i++)
        bool flag=0;
        for(int j=1;j<=n;j++)
            if(f[1][i>>dd|(1<<dis)][j]&&f[j][i&((1<<dd)-1)|(1<<dd)].count())
                flag=1;
                break;
            
        
        if(!flag&&dd!=dis)
            for(int j=1;j<=n;j++)
                if(f[1][i>>dis|(1<<dd)][j]&&f[j][i&((1<<dis)-1)|(1<<dis)].count())
                    flag=1;
                    break;
                
            
        
        ans+=flag;
    
    printf("%d\n",ans);
    return 0;
y

 

 

T3 z

咕了

 

 

不想退役就应该踏实

以上是关于9.28 csp-s模拟测试54 x+y+z的主要内容,如果未能解决你的问题,请参考以下文章

[CSP-S模拟测试]:小Y的图(最小生成树+LCA)

[CSP-S模拟测试]:取石子(博弈论+DP)

[CSP-S模拟测试]:Graph(图论+贪心)

[CSP-S模拟测试]:天空龙(模拟)

csp-s模拟测试70

[CSP-S模拟测试]:折射(DP)