• WC2016模拟Divisor


    题意:若一个数n能被表达为n的k个因数的和(因数可重复使用),则称n是k表达数

    多组测试数据T,求A~B的k表达数数量

    数据范围:A,B<=1e18,T<=5e4,2<=k<=7

    解析:这题第一眼看过去极其不可做,我考试时直接放弃

    有一个比较显然的性质,如果一个数是k表达数,那么它的倍数一定都是k表达数.

    如果n = ∑(k|n)k,那么显然有x*n = Σ(x*k|x*n)x*k;

    所以可以猜想对于每个k我们都可以找出它的几个最小k表达数xi,且任意i,j都不满足xi|xj,然后它们的所有倍数都是k表达数

    爆搜一下可以发现xi很小且很少

    如:k = 2 : 2(1 + 1)

      = 3 : 3(1 + 1+  1) 4(1 + 1 + 2)

      = 4: 4(1 + 1 + 1 + 1) 6(1 + 2 + 2 + 1) 10(2 + 2 + 5 + 1)

    即使等于7也只有15个xi,所以转化为经典问题,问A~B之间有几个数是给定的数的至少一个的倍数,容斥一下即可

    注意暴力容斥2^15 * T是过不了的,所以需要开个Map记录一下,各个最小公倍数的贡献(因为最小公倍数可能会重复,如24,54,36的最小公倍数与24,54的最小公倍数是相同的),奇加偶减即可

    代码如下:

    /*divisor*/
    #include<cstdio> 
    #include<iostream>
    #include<algorithm> 
    #include<cstring>
    #include<map>
    #include<vector>
    using namespace std;
    #define ll long long
    ll read(){
    	char c = getchar();
    	ll x = 0;
    	while(c < '0' || c > '9')	c = getchar();
    	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
    	return x;
    }
    const int maxn = 1e5 + 10;
    map<ll,ll>Map[11];
    vector<int>valid[11];
    vector<int>pr[510];
    int a[maxn];
    bool used[11][510];
    vector<int>num[11];
    vector<int>op[11];
    ll gcd(ll x,ll y){
    	return (y == 0)?x:gcd(y,x%y);
    }
    ll lcm(ll x,ll y){
    	return x / gcd(x,y) * y;
    }
    bool Dfs(int x,int n,int step,int k){
    	if(step == k + 1){
    		ll g = 0;
    		ll sum = 0;
    		for(int i = 1; i <= k; ++i)
    			g = gcd(g,a[i]),sum += a[i];
    		if(g == 1 && sum == x)	return true;
    		return false;
    	}
    	for(int i = n; i < (int)pr[x].size(); ++i){
    		int v = pr[x][i];
    		a[step] = v;
    		if(Dfs(x,i,step+1,k))		return true;
    		a[step] = 0;
    	}
    	return false;
    }
    int main(){
    	for(int i = 1; i <= 500; ++i)
    		for(int j = 1; j * i <= 500; ++j)
    			pr[j*i].push_back(i);
    	int t = read();
    	while(t--){
    		ll a = read(),b = read(),k = read();
    		if(!Map[k][0]){
    			for(int i = 1; i <= 500; ++i){
    				if(Dfs(i,0,1,k)){
    					bool flag = 1;
    					for(int j = 0; j < (int)pr[i].size(); ++j){
    						if(used[k][pr[i][j]]){
    							flag = 0;
    							break;
    						}
    					}
    					if(flag)
    						valid[k].push_back(i),used[k][i] = true;
    				}
    			}
    			Map[k][0] = 1;
    			int n = valid[k].size();
    			int tot = -1;
    			for(int i = 1; i < (1 << n); ++i){
    				ll lc = 1;
    				int cnt = 0;
    				for(int j = 0; j < n; ++j){
    					if(i & (1 << (j)))	cnt++,lc = lcm(lc,valid[k][j]);
    				}
    				int o = (cnt & 1)?1:-1;
    				if(!Map[k][lc]){
    					Map[k][lc] = ++tot;
    					num[k].push_back(lc),op[k].push_back(o);
    				}
    				else{
    					op[k][Map[k][lc]] += o;
    				}
    			}
    		}
    		ll Ans = 0;
    		for(int i = 0; i < (int)num[k].size(); ++i){
    			ll v = num[k][i],o = op[k][i];
    			Ans += o * ((b / v) - ((a - 1) / v));
    		}
    		printf("%lld
    ",Ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    Centos7搭建OpenNebula云平台
    Python中__new__和__init__的区别与联系
    16个python常用魔法函数
    微信小程序< 1 > ~ Hello 微信小程序
    扬帆起航,再踏征程(一)
    Java 社区平台
    Java 社区平台
    <Android 应用 之路> 一个类似今日头条的APP
    使用标准C读取文件遇到的结构体对齐问题及其解决办法
    编译64位cu文件的设置
  • 原文地址:https://www.cnblogs.com/y-dove/p/13449337.html
Copyright © 2020-2023  润新知