HDU 1796 How many integers can you find
题意
给你一个集合n,里面有1–n-1的整数,再给你一个集合m,里面有m个非负整数。
求集合n中能被集合m中某个数整除的数的个数。
解
很明显就是枚举因子的容斥,但是注意如果有两个因子6和9,那不能用他们相乘来容斥,应该取LCM。
正常情况下,应该是找素因子来容斥的。
这题m集合中可能会有0,要特判。
HDU4135 Co-prime
题意
给一个区间[a,b]和整数n,问区间a,b内有多少数和n互质
解
容斥删掉区间内不互质即可。
先质因数分解,求出所有素因子,注意一个素因子就算出现两次也只需要放入一个进数组就可以,比如20 = 2*2*5,只要2,5就可以
然后求[1,b]内互质的数,在求[1,a-1]互质的数,相减即可。
HDU2204 - Eddy’s爱好
题意
给你一个数n,问在1-n之中有多少个数能表示成 m^k 且k大于1。(注意1可以表示成1 = 1^2 等等,所以1也算)。
解
想到的是求 表示二次方<=n, 表示三次方<=n , 表示四次方<=n这样,但是这里显然是有重叠的比如2^6 = 4^3 = 8^2,所以和上面题一样,枚举素因子2,3,5,7,让他们作为分母来容斥
然后这里我本来是不想用pow的,但是如果用二分来求的话,很难估计上界,一个弄不好就爆long long。
就试了一下pow加了1e-7做修正,结果也能过。。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
vector<int> v;
int main()
{
vector<int> v={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};
int sz = v.size();
while (scanf("%lld",&n) != EOF)
{
ll ans = 0;
for (int i=1;i<(1<<sz);i++)
{
int cnt = 0;
int jie = 1;
bool flag=true;
for (int j=0;j<sz;j++)
if ( i & (1<<j) )
{
jie = jie * v[j],cnt++;
if (jie > 61)
{
flag = false;
break;
}
}
if (!flag) continue;
ll tmp = pow(n,1.0/jie) + 1e-7;
if (cnt & 1) ans += max(tmp-1,0ll);
else ans -= max(tmp-1,0ll);
}
printf("%lld\n",ans+1);
}
return 0;
}
CF- Gym - 100548F
题意
给你n多花,m种颜料,让你选择k种颜料,要求是必须用这k种颜料都用到,给n多花上色。问结果又多少种情况?
解
首先设最多用k种颜料的情况(不保证k种全用到),那么有
,也就是第一朵花k种情况,后面的都是k-1种情况。
然后再看最多用k-1种颜料的情况,那就是
,也就是从k种中间选择k-1种,然后就是和上面一样考虑。
仔细想想会发现,最多用k种的时候,包含了最多用k-1和k-2等等直到最多2种。
而最多用k-1种里面包含了最多用k-1种直到最多2种。那么就可以容斥了。
公式就是
然后这题需要处理阶乘的逆元
HDU-1695 GCD
题意
给你两个区间1-b和1-d,各挑一个数,问他们的GCD是k的情况有多少种,(a1,a2) 和(a2,a1)视为一种情况。
解
首先那两个数都除k的话就互质,所以可以先把b,d都除k,然后就是两个区间内找互质的数。
我一开始想的是遍历对某个区间的所有数,对每个数都在另一个区间找互质的数,可以用容斥做,但是这样显然会重复计算,而且超时。
找互质数的个数,并且还不重复的话,欧拉函数就可以,因为他是求小于它和它互质的数。所以先求一个右区间的最小值比如说是b,对这个范围内的欧拉函数求和。然后剩下的就是求(b+1,d)和(1-b)互质的数,这时候就可以用一开始容斥来做,因为
的数只能在第一个区间选到,所以不会重复。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+100;
int a,b,c,d,k,low,up,t;
int euler[MAXN];
ll ans;
vector<int> v;
void init()
{
euler[1] = 1;
for (int i=2;i<MAXN;i++)
{
if (!euler[i])
for (int j=i;j<MAXN;j+=i)
{
if (!euler[j]) euler[j]=j;
euler[j] = euler[j] / i * (i-1);
}
}
}
void getfactor(int num)
{
v.clear();
for (int i=2;i<=num/i;i++)
{
if ( num % i == 0 )
{
v.push_back(i);
while (num % i == 0) num /= i;
}
}
if (num > 1) v.push_back(num);
}
ll gethuzhi(int up)
{
ll ans = 0;
int sz = v.size();
for (int i=1;i<(1ll << sz);i++)
{
int cnt = 0;
int tmp = 1;
for (int j=0;j<sz;j++)
{
if ( i & (1ll << j) )
{
cnt ++;
tmp = tmp * v[j];
}
}
if (cnt & 1) ans += low / tmp;
else ans -= low / tmp;
}
return low - ans;
}
int main()
{
init();
scanf("%d",&t);
for (int tt=1;tt<=t;tt++)
{
ans = 0;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if ( k == 0 )
{
printf("Case %d: 0\n",tt);
continue;
}
b /= k;
d /= k;
low = min(b,d);
up = max(b,d);
long long ans = 0;
for (int i=1;i<=low;i++) ans += euler[i];
for (int i=low+1;i<=up;i++)
{
getfactor(i);
ans += gethuzhi(i);
}
printf("Case %d: %lld\n",tt,ans);
}
return 0;
}
HDU2841 Visible Trees
解
其实和上面一题是一样的,不过这里反过来也记一次。同样也是先求都包含的区间,欧拉函数求和,然后
,因为(1,1)实际上只有一个,所以要减掉。
下一步就是对(b+1,d)求(1,b)里面和它互质的个数。
要注意的是别用vector了,反复清空很慢。