【THUSC2017】【LOJ2978】杜老師 高斯消元

題目大意

  給你 \(l,r\),求從 \(l\)\(r\)\(r-l+1\) 個數中能選出多少個不一樣的子集,知足子集中全部的數的乘積是一個徹底平方數。dom

  對 \(998244353\) 取模。優化

  \(1\leq l,r\leq {10}^7\)spa

  有 \(100\) 組數據,\(\sum r-l+1\leq 6\times {10}^7\)code

題解

  對於每一個數,求出這個數中包含了哪些出現次數爲奇數的質數。ip

  那麼就能夠直接高斯消元,記矩陣的秩爲 \(r\),答案就是 \(2^{r-l+1-r}\)。能夠用 bitset 優化。get

  時間複雜度爲 \(O(\frac{n\pi(n)^2}{w})\)string

  能夠發現,一個數最多有一個 \(>\sqrt r\) 的質因子。那麼對於兩個最大值因子相同的數,能夠讓第二個數的狀態異或上第一個數的狀態,這樣第二個數的狀態就只有 \(\leq \sqrt r\) 的質因子了。it

  這樣就能夠讓矩陣的列的數量下降到 \(\pi(\sqrt n)\)io

  可是仍是過不了這題。function

  能夠發現,當 \(r-l+1\) 足夠大的時候就能夠認爲這個矩陣滿秩了。在本題中,當 \(r-l+1>6000\) 的時候就能夠不用高斯消元直接求出答案了。

  時間複雜度:\(O(\frac{T\times 6000\times \pi(\sqrt n)^2}{w})\)

  這個複雜度很鬆,實際跑起來很是快。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
#include<bitset>
using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
    char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const ll p=998244353;
const int N=10000010;
const int n=10000000;
const int sqrtn=3162;
const int size=446;
typedef bitset<500> arr;
ll fp(ll a,ll b)
{
    ll s=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1)
            s=s*a%p;
    return s;
}
int c[N],d[N],b[N],pri[N],cnt;
void sieve()
{
    c[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!b[i])
        {
            pri[++cnt]=i;
            d[i]=cnt;
            c[i]=i;
        }
        for(int j=1;j<=cnt&&i*pri[j]<=n;j++)
        {
            int v=i*pri[j];
            b[v]=1;
            c[v]=c[i];
            if(i%pri[j]==0)
                break;
        }
    }
}
void init()
{
    sieve();
}
arr get(int x)
{
    while(c[x]>sqrtn)
        x/=c[x];
    arr res;
    while(x>1)
    {
        res.flip(d[c[x]]-1);
        x/=c[x];
    }
    return res;
}
int len,tot,tot2;
void solve2(int l,int r)
{
    tot=0;
    len=r-l+1;
    for(int i=1;i<=cnt;i++)
        if(r/pri[i]!=(l-1)/pri[i])
            tot++;
    ll ans=fp(2,len-tot);
    printf("%lld\n",ans);
}
arr e[size];
int insert(arr v)
{
    for(int i=0;i<size;i++)
        if(v[i])
        {
            if(e[i][i])
                v^=e[i];
            else
            {
                e[i]=v;
                return 1;
            }
        }
    return 0;
}
pii a[10000];
arr pre;
int cmp(pii a,pii b)
{
    return a.second<b.second;
}
void solve()
{
    int l,r;
    scanf("%d%d",&l,&r);
    if(r-l>6000)
    {
        solve2(l,r);
        return;
    }
    tot=0;
    tot2=0;
    len=r-l+1;
    if(l==1)
        l++;
    int m=0;
    for(int i=0;i<size;i++)
        e[i].reset();
    for(int i=l;i<=r;i++)
        a[++m]=pii(i,c[i]);
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;i++)
        if(a[i].second<=sqrtn)
        {
            if(tot<size)
                if(insert(get(a[i].first)))
                    tot++;
        }
        else if(i==1||a[i].second!=a[i-1].second)
        {
            tot2++;
            if(tot<size)
                pre=get(a[i].first);
        }
        else
        {
            if(tot<size)
                if(insert(get(a[i].first)^pre))
                    tot++;
        }
    ll ans=fp(2,len-tot-tot2);
    printf("%lld\n",ans);
}
int main()
{
    open("dls");
    init();
    int t;
    scanf("%d",&t);
    while(t--)
        solve();
    return 0;
}
相關文章
相關標籤/搜索