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

题目大意

  给你 \(l,r\),求从 \(l\)\(r\)\(r-l+1\) 个数中能选出多少个不同的子集,满足子集中所有的数的乘积是一个完全平方数。

  对 \(998244353\) 取模。

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

  有 \(100\) 组数据,\(\sum r-l+1\leq 6\times {10}^7\)

题解

  对于每个数,求出这个数中包含了哪些出现次数为奇数的质数。

  那么就可以直接高斯消元,记矩阵的秩为 \(rank\),答案就是 \(2^{r-l+1-rank}\)。可以用 bitset 优化。

  时间复杂度为 \(O(\frac{n\pi(n)^2}{w})\)

  可以发现,一个数最多有一个 \(>\sqrt r\) 的质因子。那么对于两个最大质因子相同的数,可以让第二个数的状态异或上第一个数的状态,这样第二个数的状态就只有 \(\leq \sqrt r\) 的质因子了。

  这样就可以让矩阵的列的数量降低到 \(\pi(\sqrt n)\)

  但是还是过不了这题。

  可以发现,当 \(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;
}
posted @ 2019-01-20 12:18  ywwyww  阅读(517)  评论(3编辑  收藏  举报