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

T1 x

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

但是直接分解会$T$,埃筛是个很好的选择,或者利用每个数最多只会有1个大于$\sqrt{n}$的质因子,线筛$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

咕了

 

 

不想退役就应该踏实

转载于:https://www.cnblogs.com/jrf123/p/11611362.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值