LOJ #2978「THUSCH 2017」杜老师

听说LOJ传了THUSC题赶紧上去看一波

随便点了一题都不会做想了好久才会写暴力爆了一发过了...

LOJ #2978


题意

$ T$次询问,每次询问$ L,R$,问有多少种选取区间中数的方案使得选出的数的积为完全平方数

$ T \leq 100,R \leq 10^7 \sum\limits R-L \leq 6·10^7$

时限$ 5s$


 

题解

随便写个暴力发现答案都是$2$的若干次幂

首先对于每个数,每个质因子出现的次数显然只有奇偶性是有用的

用一个$ bitset$存储每个数中每个质因数的出现次数的奇偶性

则问题转化成取若干个数使得异或和为$ 0$

用线性基维护这个问题

答案则为$ 2^{自由基的数量}$

其中自由基就是可以被其他基所表示的基

 

直接这么暴力复杂度太大

考虑若一个质因数的平方超过$ 10^7$那它不可能在一个数中出现两次

因此我们只统计前$ \sqrt{10^7}$个数中的质因数(446个)的$ bitset$

统计答案的时候我们先将区间内数按其最大质因数排序(若一个数的最大质因数不超过 $\sqrt{10^7}$则认为是0)

对于一段最大质因数相同的区间,我们钦定第一个数为非自由基

则这段区间的其他拥有这个质因数的数可以异或上这个钦定的非自由基

以起到消掉这个额外质因数的效果

 

这样暴力做的复杂度是$ O(6·10^7·450·\frac{450}{64})$的

显然过不了

如果加个剪枝:若当前线性基已满则直接跳出判断 就能过了...

虽然跑的超级慢...

 

这题有个科技:

若$ R-L+1$超过了一个定值(可定为$ 2 \sqrt{10^7}$),则对每个质数,我们只要选一个数作为这个数的基底

然后所有其他非基底的区间中的数都能被这些基底表示

即答案为$ 2^{R-L+1-出现过的质数数量}$

然后就跑的飞快了...


代码

 去掉Solve就是暴力

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<bitset>
#define p 998244353
#define file(x)freopen(x".in","r",stdin);freopen(x".out","w",stdout)
#define rt register int
#define l putchar('\n')
#define ll long long
#define r read()
using namespace std;
inline ll read(){
    ll x=0;char zf=1;char ch=getchar();
    while(ch!='-'&&!isdigit(ch))ch=getchar();
    if(ch=='-')zf=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int k,m,n,x,y,z,cnt,ans,val=10000000;
bool pri[10000010];int ss[4010],id[4010],lp[10000010],zs[1000010],tot;
bitset<449>a[449];
int q[10000010],sl;
bitset<449>make(int x){
    bitset<449>A;
    if(lp[x])x/=lp[x];
    for(rt i=1;ss[i]*ss[i]<=x&&i<=cnt;i++)while(x%ss[i]==0)
    A.flip(i),x/=ss[i];
    if(x>1)A.flip(id[x]);
    return A;
}
vector<int>jw[5000010];
void solve(int L,int R){
    int gs=R-L+1;
    for(rt i=1;i<=tot&&zs[i]<=R;i++)if(R/zs[i]!=(L-1)/zs[i])gs--;
    int ans=1;
    for(rt x=2,i=gs;i;i>>=1,x=1ll*x*x%p)if(i&1)
    ans=1ll*ans*x%p;writeln(ans);
}
int main(){
    n=r;if(n<=10)val=1000000;
    const int v=sqrt(val);
    for(rt i=2;i<=val;i++)if(!pri[i]){
        if(i<=v)ss[++cnt]=i,id[i]=cnt;
        zs[++tot]=i;
        if(i<=v)for(rt j=i*i;j<=val;j+=i)pri[j]=1;
        if(i>v)for(rt j=i;j<=val;j+=i)lp[j]=i;
    }
    bitset<449>now;
    while(n--){
        memset(a,0,sizeof(a));
        int L=r,R=r,ans=1;int len=0; 
        if(R-L>7000){
            solve(L,R);
            continue;
        }
        for(rt i=L;i<=R;i++)if(lp[i]<=5000000)jw[lp[i]].push_back(i);
        for(rt i=L;i<=R;i++)if(lp[i]<=5000000){
            for(rt j=0;j<jw[lp[i]].size();j++)q[++len]=jw[lp[i]][j];
            jw[lp[i]].clear();
        }
        sl=0;now&=0;
        for(rt i=1;i<=len;i++){
            const int v=q[i];
            if(lp[v]!=lp[q[i-1]]&&i>1)now=make(v);
            else {    
                if(sl==cnt){ans=1ll*ans*2%p;continue;}        
                bitset<449>g=make(v)^now;        
                if(g.count()==0){ans=1ll*ans*2%p;continue;}
                for(rt j=448;j>=0;j--)if(g[j]){
                    if(a[j][j]){
                        if(g==a[j]){ans=1ll*ans*2%p;break;}
                        g^=a[j];
                    }
                    else {a[j]=g;sl++;break;}
                }
            }
        }
        writeln(ans);
    }
    return 0;
}

 

posted @ 2019-01-18 08:02  Kananix  阅读(892)  评论(0编辑  收藏  举报

Contact with me