【校内模拟】pow(容斥)(搜索优化)

简要题意:

1 ≤ x ≤ n , 1 ≤ y ≤ m 1\leq x\leq n,1\leq y\leq m 1xn,1ym x y x^y xy 有多少种不同的结果。

n , m ≤ 5 e 18 n,m\leq 5e18 n,m5e18


题解:

原题TCO2013 2A 1000,数据范围只有 1e9。

其实这个数据范围 5 e 18 5e18 5e18 没有什么意思,不过是一些诡异的搜索优化,没有通用性和参考价值,不过这里还是说一下。

考虑除了 1 1 1 的剩下 n − 1 n-1 n1 个数,全部 m m m 次方之后扣掉重复的。

s z [ i ] sz[i] sz[i] 为开 i i i 次方之后不能再开任意 i > 1 i > 1 i>1 次方的数的数量,这个随便算算即可,也是容斥。

然后考虑 s z [ i ] ( i > 1 ) sz[i](i > 1) sz[i](i>1) 集合中的数,我们考虑扣掉它的贡献和 i i i 更小的集合的贡献的重复部分,就是说计算 i × [ 1 , m ] i \times [1,m] i×[1,m] [ 1 , i ) × [ 1 , m ] [1, i)\times [1,m] [1,i)×[1,m] 中有多少个重复的。

枚举 j < i j < i j<i 考虑计算 i × [ 1 , m ] i\times[1,m] i×[1,m] j × [ 1 , m ] j \times [1,m] j×[1,m] ,且没有在 j < k < i j < k < i j<k<i k × [ 1 , m ] k \times [1,m] k×[1,m] 中出现的数个数。

考虑在 j × [ 1 , m ] j\times [1,m] j×[1,m] 的集合里面进行容斥。

首先注意到在 [ 1 , m ] [1,m] [1,m] 里面选的数乘上 j j j 之后必须是 i i i 的倍数,也就是只考虑 i / g c d ( i , j ) i/gcd(i,j) i/gcd(i,j) 的倍数。

如果这个数再要在 k ∗ [ 1 , m ] k * [1,m] k[1,m] 这个 集合里面出现,又需要一个 l c m lcm lcm,就是 l c m ( i , j , k ) / j lcm(i,j,k)/j lcm(i,j,k)/j 的倍数。

这样算下来数很大,容斥的时候很快达到上界,可以跑 5 e 18 5e18 5e18

然后就是原题的优化,拎出来把整除的丢掉。容斥也不要用 dfs ,直接归并,因为质因子数有限,状态数不会太多。

最后一个优化是考虑 i / g c d ( i , j ) i/gcd(i,j) i/gcd(i,j) 和容斥用的数的集合,这个也有一部分重复,用个 m a p < p a i r , i n t > map<pair,int> map<pairint> 存一下一起算 。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;
using pli=std::pair<ll,int>;
#define fi first
#define se second

cs int mod=1004535809;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;} 
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);} 
inline int MD(ll x){return x>=mod?x%mod:(x<0?x%mod+mod:x);}

bool cmp(ll y,int k,ll x){
	for(int re i=1;i<=k;++i){
		x/=y;if(!x)return false;
	}return true;
}

ll sqrt(ll x,int k){
	ll y=std::pow(x,1./k);
	while(!cmp(y,k,x))--y;
	while(cmp(y,k,x))++y;--y;
	return y;
}

void merge(std::vector<pli> &a,std::vector<pli> &b,std::vector<pli> &res){
	size_t ah=0,bh=0;res.clear();
	while(ah<a.size()&&bh<b.size()){
		if(a[ah].fi<b[bh].fi)
			res.push_back(a[ah++]);
		else if(a[ah].fi>b[bh].fi)
			res.push_back(b[bh++]);
		else {
			if(add(a[ah].se,b[bh].se))
				res.push_back(pli(a[ah].fi,
					add(a[ah].se,b[bh].se)));
			++ah,++bh;
		}
	}while(ah<a.size())res.push_back(a[ah++]);
	while(bh<b.size())res.push_back(b[bh++]);
}

void merge_all(std::vector<pli> *h,int n,std::vector<pli> &res){
	std::vector<pli> vec;auto tmp=&vec;
	std::priority_queue<std::pair<int,std::vector<pli>*> >q;
	for(int re i=0;i<n;++i)if(h[i].size())
		q.push({-(int)h[i].size(),&h[i]});
	if(q.empty()){res.clear();return;}
	if(q.size()==1){res=*q.top().se;return;}
	while(q.size()>1){
		auto t1=q.top().se;q.pop();
		auto t2=q.top().se;q.pop();
		if(q.empty())tmp=&res;
		merge(*t1,*t2,*tmp);
		q.push({-(int)tmp->size(),tmp});
		tmp=t1;
	}
}

ll n,m;
int sz[70],coef[70][70];
int id[70][70],dn[70];
std::vector<pli> h,hs[70];

void Main(){
	scanf("%lld%lld",&n,&m);int mx=0;
	while((n>>mx)>1)++mx;
	for(int re i=mx;i;--i){
		sz[i]=MD(sqrt(n,i)-1);
		for(int re j=i+i;j<=mx;j+=i)
			Dec(sz[i],sz[j]);
	}for(int re i=1;i<=mx;++i)
		for(int re j=0;j<i;++j)
			coef[i][j]=i/std::__gcd(i,j);
	int ans=add(1,mul(MD(n-1),MD(m)));
	std::map<pli,int> mp;
	for(int re j=2;j<=mx;++j)
		for(int re k=1;k<j;++k){
			int div=j/std::__gcd(j,k);
			std::vector<int> d;ll st=0;
			for(int re l=k+1;l<j;++l)
				d.push_back(l/std::__gcd(div*k,l));
			std::sort(d.begin(),d.end());int ct=0;
			for(size_t re i=0;i<d.size();++i){
				bool fl=false;
				for(size_t re l=0;l<i&&!fl;++l)
					fl=d[i]%d[l]==0;
				if(!fl)st|=1ll<<d[i],++ct;
			}Inc(mp[pli(st,div)],sz[j]);
		}
	for(int re i=1;i<=mx;++i)
		for(int re j=1;j<=i;++j)
			if(i%j==0)id[i][j]=dn[i]++;
	for(cs auto &t:mp){
		h.clear();h.push_back(pli(1,1));
		ll lim=m/t.fi.se;
		for(int re i=1;i<=mx;++i)
			if((t.fi.fi>>i)&1){
				for(int re j=0;j<dn[i];++j)
					hs[j].clear();
				for(auto &p:h){
					int r=coef[i][p.fi%i];
					if(r>1){
						hs[0].push_back(p);
						if(p.fi<=lim/r)
							hs[id[i][r]].push_back({p.fi*r,dec(0,p.se)});
					}
				}merge_all(hs,dn[i],h);
			}
		int vl=0;
		for(auto &p:h)
			Inc(vl,mul(p.se,MD(lim/p.fi)));
		Dec(ans,mul(t.se,vl));
	}
	cout<<ans<<"\n";
}

inline void file(){
#ifdef zxyoi 
	freopen("pow.in","r",stdin);
#else
	freopen("pow.in","r",stdin);
	freopen("pow.out","w",stdout);
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值