【转】【SNOI 2017】遗失的答案

Solution

https://www.luogu.com.cn/blog/MachineryCountry/solution-p5366
https://www.luogu.com.cn/blog/m-sea/solution-p5366

全集含义:整个集合里面所有质因子都有没有的数,所有质因子都有一个数存在那个质因子的指数与最小公倍数相等。

这里的对位是 \(S\)

Code

#pragma GCC optimize(2)
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

const int mod = 1e9 + 7;

int cnt, lim, n, G, l, q, pc, p[10], c[10], num[650], sum[650], s[1 << 16], f[650][1 << 16], g[650][1 << 16], h[650][1 << 16], ans[650];
vector <int> v;

int read() {
	int x = 0, f = 1; char s;
	while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
	while(s >= '0' && s <= '9') x = (x << 1) + (x << 3) + (s ^ 48), s = getchar();
	return x * f;
}

int inc(int x, const int y) {
	x = x + y;
	if(x >= mod) x -= mod;
	return x;
}

int sub(int x, const int y) {
	x = x - y;
	if(x < 0) x += mod;
	return x;
}

int qkpow(int x, int y) {
    int r = 1;
    while(y) {
        if(y & 1) r = 1ll * r * x % mod;
        x = 1ll * x * x % mod; y >>= 1;
    }
    return r;
}

void divide(int x) {
    for(int i = 2; i * i <= x; ++ i) {
        if(x % i) continue;
        p[++ pc] = i;
        while(x % i == 0) x /= i, ++ c[pc];
    }
    if(x > 1) p[++ pc] = x, ++ c[pc];
}

void FWT(int *f, const int op = 1) {
	for(int i = 2; i <= lim; i <<= 1)
		for(int j = 0, p = i >> 1; j < lim; j += i)
			for(int k = j; k < j + p; ++ k)
				if(op == 1) f[k + p] = inc(f[k + p], f[k]);
				else f[k + p] = sub(f[k + p], f[k]);
}

int cal(int x) {
	int r = 0, tot;
	for(int i = 1; i <= pc; ++ i) {
		tot = 0;
		while(x % p[i] == 0) x /= p[i], ++ tot;
		if(tot == 0) r |= 1 << i - 1;
		if(tot == c[i]) r |= 1 << i - 1 + pc;
	}
	return r;
}

int main() {
	int x;
	n = read(), G = read(), l = read(), q = read();
    if(l % G) {while(q --) puts("0"); return 0;}
    l /= G, n /= G; divide(l);
    for(int i = 1; i <= n && i * i <= l; ++ i) {
    	if(l % i) continue;
    	v.push_back(i);
    	if(i * i != l && l / i <= n) v.push_back(l / i);
	}
	for(int i = 0, siz = v.size(); i < siz; ++ i) ++ s[cal(v[i])];
	lim = 1 << (pc << 1);
	for(int i = 0; i < lim; ++ i)
		if(s[i]) num[++ cnt] = i, sum[cnt] = qkpow(2, s[i]) - 1;//去掉全不选
	f[0][0] = g[cnt + 1][0] = 1;
	for(int i = 1; i <= cnt; ++ i)
		for(int S = 0; S < lim; ++ S) {
			f[i][S] = inc(f[i][S], f[i - 1][S]);
			f[i][S | num[i]] = inc(f[i][S | num[i]], 1ll * sum[i] * f[i - 1][S] % mod);
		}
	for(int i = cnt; i; -- i)
		for(int S = 0; S < lim; ++ S) {
			g[i][S] = inc(g[i][S], g[i + 1][S]);
			g[i][S | num[i]] = inc(g[i][S | num[i]], 1ll * sum[i] * g[i + 1][S] % mod);
		}
	for(int i = 0; i <= cnt; ++ i) FWT(f[i]);
	for(int i = 1; i <= cnt + 1; ++ i) FWT(g[i]);
	for(int i = 1; i <= cnt; ++ i)
		for(int S = 0; S < lim; ++ S)
			h[i][S] = 1ll * f[i - 1][S] * g[i + 1][S] % mod;
	for(int i = 1; i <= cnt; ++ i)  FWT(h[i], -1);
	for(int i = 1; i <= cnt; ++ i) {
		for(int S = 0; S < lim; ++ S)
			if((S | num[i]) == lim - 1) ans[i] = inc(ans[i], h[i][S]);
		ans[i] = 1ll * ans[i] * qkpow(2, s[num[i]] - 1) % mod;
	}
	while(q --) {
		x = read();
		if(x % G) {puts("0"); continue;}
		x /= G;
		if(l % x || x > n) {puts("0"); continue;}
		printf("%d\n", ans[lower_bound(num + 1, num + cnt + 1, cal(x)) - num]);
	}
	return 0;
}
posted on 2020-06-15 22:12  Oxide  阅读(101)  评论(0)    收藏  举报