还都是比较可做的。
奇怪的背包
对于每一个物品
i
i
i,他能拼出的物品
d
d
d满足
d
∣
g
c
d
(
P
,
V
[
i
]
)
d|gcd(P,V[i])
d∣gcd(P,V[i]),所以一个物品只需要对
P
P
P去
g
c
d
gcd
gcd即可。
根据蜚蜀定理你一堆物品能拼出的物品为这堆物品的
g
c
d
gcd
gcd的倍数。
然后你做一个
d
p
dp
dp表示,
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个物品最大公约数为
j
j
j的方案数。
然后对于每个约数枚举倍数搞一遍就好了。
#include <map>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
int plen, p[1600];
LL f[1600][1600], g[1600];
int s[1600];
map<int, int> mp;
int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}
LL pow_mod(LL a, LL k) {
LL ans = 1;
while(k) {
if(k & 1) (ans *= a) %= mod;
(a *= a) %= mod; k /= 2;
} return ans;
}
int main() {
int n = read(), Q = read(), P = read(); int u = P;
for(int i = 1; i <= sqrt(u); i++) {
if(u % i == 0) {
p[++plen] = i;
p[++plen] = u / i;
}
} if(p[plen] * p[plen] == u) --plen;
sort(p + 1, p + plen + 1);
for(int i = 1; i <= plen; i++) mp[p[i]] = i;
for(int i = 1; i <= n; i++) {
int x = gcd(read(), P);
++s[mp[x]];
} f[0][plen] = 1;
for(int i = 1; i <= plen; i++) {
for(int j = 1; j <= plen; j++) if(f[i - 1][j]){
(f[i][j] += f[i - 1][j]) %= mod;
(f[i][mp[gcd(p[j], p[i])]] += f[i - 1][j] * (pow_mod(2, s[i]) - 1) % mod) %= mod;
}
} for(int i = 1; i <= plen; i++) g[i] = (f[plen][i] + mod) % mod;
for(int i = plen - 1; i >= 1; i--) {
for(int j = i + 1; j <= plen; j++) if(p[j] % p[i] == 0){
(g[j] += g[i]) %= mod;
}
} for(int i = 1; i <= Q; i++) {
int x = gcd(P, read());
printf("%d\n", g[mp[x]]);
}
return 0;
}
反色游戏
先考虑无解,对于任意一个连通块,假如
1
1
1的个数为奇数,就无解,否则有解。
因为你想,假如你选了一条边,奇偶性无论如何都是不变的。
否则你的方案数就是
2
m
−
n
+
p
2^{m-n+p}
2m−n+p,
p
p
p为联通块个数,
n
n
n为点数,
m
m
m为边数。
你假设当前的边已经构成了一个森林,且满足与原图连通性相同,
m
−
n
+
p
m-n+p
m−n+p即未加入的边数,每加入一条边,他都可以选择反转两个端点的颜色或不反转,因为这样奇偶性并不改变。
你考虑用强连通维护每个点的情况,要求出每一个点去掉他会有多少个新的连通块,以及分成的新联通快的奇偶性什么的。。。
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 100001;
const LL mod = 1e9 + 7;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void put(int x) {
if(x >= 10) put(x / 10);
putchar(x % 10 + '0');
}
struct edge {
int x, y, next;
} e[2 * N]; int len, last[N];
int id, cnt, now, dfn[N], low[N], cut[N], tot[N], hh[N];
int bin[N], du[N], belong[N];
char ss[N];
bool v[N];
void ins(int x, int y) {
e[++len].x = x, e[len].y = y;
e[len].next = last[x], last[x] = len;
}
void tarjan(int x, int fa) {
low[x] = dfn[x] = ++id; belong[x] = now;
v[x] = 1, tot[x] = ss[x] == '1';
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(!dfn[y]) {
tarjan(y, x);
tot[x] += tot[y];
if(low[y] >= dfn[x]) {
cut[x]++; v[x] &= (tot[y] & 1) == 0;
hh[x] += tot[y];
} else low[x] = _min(low[x], low[y]);
} else if(y != fa) low[x] = _min(low[x], dfn[y]);
} if(!fa) cut[x]--;
}
int main() {
bin[0] = 1; for(int i = 1; i < N; i++) bin[i] = (bin[i - 1] * 2) % mod;
int tt = read();
while(tt--) {
int n = read(), m = read();
int ans = 0;
memset(du, 0, sizeof(du));
len = 0; memset(last, 0, sizeof(last));
memset(dfn, 0, sizeof(dfn)), memset(low, 0, sizeof(low));
memset(belong, 0, sizeof(belong)), memset(cut, 0, sizeof(cut));
memset(v, 0, sizeof(v)), memset(hh, 0, sizeof(hh)), memset(tot, 0, sizeof(tot));
memset(belong, 0, sizeof(belong));
for(int i = 1; i <= m; i++) {
int x = read(), y = read();
ins(x, y), ins(y, x), du[x]++, du[y]++;
} scanf("%s", ss + 1);
cnt = id = 0;
int bk = 0;
for(int i = 1; i <= n; i++) if(!dfn[i]) {
++cnt, now = i, tarjan(i, 0);
bk += tot[i] & 1;
} int sum = m - n + cnt;
if(bk) putchar('0'), putchar(' ');
else put(bin[sum]), putchar(' ');
for(int i = 1; i <= n; i++) {
if(!du[i]) {
if(bk - (ss[i] == '1')) putchar('0'), putchar(' ');
else put(bin[sum]), putchar(' ');
} else {
if(v[i] && !(bk - (tot[belong[i]] & 1)) && !((tot[belong[i]] - (ss[i] == '1') - hh[i]) & 1)) put(bin[sum - du[i] + cut[i] + 1]), putchar(' ');
else putchar('0'), putchar(' ');
}
} puts("");
}
return 0;
}
字串覆盖
考虑
r
−
l
+
1
>
=
50
r-l+1>=50
r−l+1>=50的部分,
先求一边
S
A
SA
SA,建一个主席树。
你可以对于一个串二分出他在
S
A
SA
SA数组中的范围,然后你相当于在主席树中寻找下一个这个串出现的位置。
发现对于
r
−
l
+
1
>
=
2000
r-l+1>=2000
r−l+1>=2000,需要这样暴力求
n
x
t
nxt
nxt的次数很少其实很少直接暴力做。
然后
50
<
=
r
−
l
+
1
<
=
2000
50<=r-l+1<=2000
50<=r−l+1<=2000,因为随机什么的,次数又有限制,所以很快。
对于
r
−
l
+
1
<
=
50
r-l+1<=50
r−l+1<=50,我们对于每一个点每一个长度求一个
n
x
t
nxt
nxt,然后用倍增优化暴力找
n
x
t
nxt
nxt的过程。
这样预处理的时间复杂度是
O
(
50
∗
l
o
g
n
∗
n
)
O(50*logn*n)
O(50∗logn∗n)但实际上是可以做到不带那个
l
o
g
log
log,
C
l
a
r
i
s
Claris
Claris使用了一种基数排序的方式将复杂度做到
O
(
50
∗
n
)
O(50*n)
O(50∗n)预处理。
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 100001;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void put(LL x) {
if(x >= 10) put(x / 10);
putchar(x % 10 + '0');
}
struct tnode {
int lc, rc, c;
} t[N * 20]; int cnt, rt[N * 2];
int z, Rsort[N * 2], Rank[N * 2], sa[N * 2], tt[N * 4], height[N * 2];
int a[N * 2], bin[20], Log[N * 2];
char s1[N], s2[N];
struct st_table {
int n, f[18][N * 2];
void bt() {
for(int i = 1; i <= z; i++) f[0][i] = height[i];
for(int i = 1; bin[i] <= z; i++) {
for(int j = 1; j <= z - bin[i] + 1; j++) {
f[i][j] = _min(f[i - 1][j], f[i - 1][j + bin[i - 1]]);
}
}
}
int query(int l, int r) {
l++;
int hh = Log[r - l + 1];
return _min(f[hh][l], f[hh][r - bin[hh] + 1]);
}
} st;
struct node {int s, t, l, r;} q[N];
struct beizeng {
vector<int> q[N];
int n, f[17][N], a[N];
LL s[N];
void dfs(int x) {
s[x] += x;
for(int k = 0; k < q[x].size(); k++) {
int y = q[x][k];
s[y] = s[x], dfs(y);
}
}
void bt() {
for(int i = 1; i <= n; i++) f[0][i] = a[i], q[i].clear();
for(int i = 1; bin[i] <= n; i++) {
for(int j = 1; j <= n - bin[i] + 1; j++) {
f[i][j] = f[i - 1][f[i - 1][j]];
}
} for(int i = 1; i <= n; i++) q[a[i]].push_back(i);
dfs(0);
}
} g;
vector<int> o[N];
LL ans[N];
void Link(int &u, int l, int r, int p) {
if(!u) u = ++cnt;
t[u].c++;
if(l == r) return ;
int mid = (l + r) / 2;
if(p <= mid) Link(t[u].lc, l, mid, p);
else Link(t[u].rc, mid + 1, r, p);
}
void Merge(int &u1, int u2) {
if(!u1 || !u2) {u1 += u2; return ;}
t[u1].c += t[u2].c;
Merge(t[u1].lc, t[u2].lc);
Merge(t[u1].rc, t[u2].rc);
}
int fl(int u1,int u2, int l, int r, int ll, int rr) {
if(!(t[u1].c - t[u2].c)) return 0;
if(l == r) return l;
int mid = (l + r) / 2;
if(rr <= mid) return fl(t[u1].lc, t[u2].lc, l, mid, ll, rr);
else if(ll > mid) return fl(t[u1].rc, t[u2].rc, mid + 1, r, ll, rr);
else {
int s = fl(t[u1].lc, t[u2].lc, l, mid, ll, mid);
if(s == 0) return fl(t[u1].rc, t[u2].rc, mid + 1, r, mid + 1, rr);
return s;
}
}
void get_sa() {
memcpy(Rank, a, sizeof(Rank));
int m = 30;
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= z; i++) ++Rsort[Rank[i]];
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = z; i >= 1; i--) sa[Rsort[Rank[i]]--] = i;
int ln = 1, p = 0;
while(p < z) {
int tp = 0;
for(int i = z - ln + 1; i <= z; i++) tt[++tp] = i;
for(int i = 1; i <= z; i++) if(sa[i] - ln > 0) tt[++tp] = sa[i] - ln;
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= z; i++) ++Rsort[Rank[tt[i]]];
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = z; i >= 1; i--) sa[Rsort[Rank[tt[i]]]--] = tt[i];
for(int i = 1; i <= z; i++) tt[i] = Rank[i];
p = 1; Rank[sa[1]] = 1;
for(int i = 2; i <= z; i++) {
if(tt[sa[i]] != tt[sa[i - 1]] || tt[sa[i] + ln] != tt[sa[i - 1] + ln]) ++p;
Rank[sa[i]] = p;
} ln *= 2, m = p;
}
}
void get_height() {
int p = 0;
for(int i = 1; i <= z; i++) {
int j = sa[Rank[i] - 1];
if(p) p--;
while(a[i + p] == a[j + p]) p++;
height[Rank[i]] = p;
}
}
int findl(int p, int len) {
int l = 1, r = p - 1, hh = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(st.query(p - mid, p) >= len) l = mid + 1, hh = mid;
else r = mid - 1;
} return p - hh;
}
int findr(int p, int len) {
int l = 1, r = z - p, hh = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(st.query(p, p + mid) >= len) l = mid + 1, hh = mid;
else r = mid - 1;
} return p + hh;
}
int main() {
int n = read(), K = read();
scanf("%s%s", s1 + 1, s2 + 1);
for(int i = 1; i <= n; i++) a[i] = s1[i] - 'a' + 2;
a[n + 1] = 1;
for(int i = 1; i <= n; i++) a[i + n + 1] = s2[i] - 'a' + 2;
z = 2 * n + 1, get_sa(), get_height();
bin[0] = 1;for(int i = 1; i <= 19; i++) bin[i] = bin[i - 1] << 1;
Log[1] = 0; for(int i = 2; i <= z; i++) Log[i] = Log[i >> 1] + 1;
st.n = z, st.bt();
for(int i = 1; i <= z; i++) {
if(sa[i] <= n) Link(rt[i], 1, n, sa[i]);
Merge(rt[i], rt[i - 1]);
} int m = read();
for(int i = 1; i <= m; i++) {
q[i].s = read(), q[i].t = read(), q[i].l = read(), q[i].r = read();
int len = q[i].r - q[i].l + 1;
if(len <= q[i].t - q[i].s + 1) o[len].push_back(i);
}
for(int len = 1; len <= 50; len++) {
int pp = o[len].size();
if(!pp) continue;
g.n = n;
for(int j = 1; j <= n; j++) {
int ll = findl(Rank[j], len), rr = findr(Rank[j], len);
if(j + len > n - len + 1) break;
int gg = fl(rt[rr], rt[ll - 1], 1, n, j + len, n - len + 1);
g.a[j] = gg;
} g.bt();
for(int z = 0; z < pp; z++) {
int i = o[len][z];
int s = q[i].s, t = q[i].t, l = q[i].l, r = q[i].r;
int ll = findl(Rank[n + l + 1], r - l + 1), rr = findr(Rank[n + l + 1], r - l + 1);
int gg = fl(rt[rr], rt[ll - 1], 1, n, s, t - len + 1);
if(gg == 0) ans[i] = 0;
else {
LL s1 = 1, s2; int cc = gg;
for(int j = 16; j >= 0; j--) if(g.f[j][gg] && g.f[j][gg] + len - 1 <= t){
s1 += bin[j];
gg = g.f[j][gg];
} s2 = g.s[cc] - g.s[g.a[gg]];
ans[i] = s1 * K - s2;
}
}
} for(int len = 50 + 1; len <= m; len++) {
for(int z = 0; z < o[len].size(); z++) {
int i = o[len][z];
int s = q[i].s, t = q[i].t, l = q[i].l, r = q[i].r;
int ll = findl(Rank[n + l + 1], r - l + 1), rr = findr(Rank[n + l + 1], r - l + 1);
int gg = fl(rt[rr], rt[ll - 1], 1, n, s, t - len + 1);
if(gg == 0) ans[i] = 0;
else {
LL s1 = 1, s2 = gg;
while(1) {
if(gg + len > t - len + 1) break;
gg = fl(rt[rr], rt[ll - 1], 1, n, gg + len, t - len + 1);
if(gg == 0) break;
s1++, s2 += gg;
} ans[i] = s1 * K - s2;
}
}
} for(int i = 1; i <= m; i++) put(ans[i]), puts("");
return 0;
}
苹果树
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
LL mod;
LL C[2100][2100], jc[2100];
int main() {
int n; scanf("%d%lld", &n, &mod);
for(int i = 0; i <= n; i++) C[i][0] = 1;
for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
jc[0] = 1; for(int i = 1; i <= n; i++) jc[i] = (LL)jc[i - 1] * i % mod;
LL ans = 0;
for(int i = 2; i <= n; i++) {
LL hh = jc[i];
for(int j = n - i + 1; j >= 1; j--) {
LL u = jc[j] * C[n - i][j - 1] % mod * hh % mod;
(ans += (LL)j * (n - j) % mod * u % mod) %= mod;
(hh *= (LL)n - j) %= mod;
}
} printf("%lld\n", ans);
return 0;
}
染色
设
g
[
i
]
g[i]
g[i]为至少有
i
i
i种颜色的方案数,这个东西很好求。
然后直接二项式反演即可。
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int M = 100002, N = 10000001;
const LL mod = 1004535809;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void put(int x) {
if(x >= 10) put(x / 10);
putchar(x % 10 + '0');
}
int w[M], R[M * 4];
int jc[N], inv[N], f[M * 4], g[M * 4];
int w1[M * 4], w2[M * 4];
int pow_mod(int a, int k) {
int ans = 1;
while(k) {
if(k & 1) ans = (LL)ans * a % mod;
a = (LL)a * a % mod; k /= 2;
} return ans;
}
int C(int n, int m) {
if(n < m) return 0;
return (LL)jc[n] * inv[m] % mod * inv[n - m] % mod;
}
void pre(int len) {
for(int i = 1; i < len; i <<= 1) {
w1[i] = pow_mod(3, (mod - 1) / (i << 1));
w2[i] = pow_mod(w1[i], mod - 2);
}
}
int add(int x, int y) {
x += y;
while(x >= mod) x -= mod;
return x;
}
void NTT(int y[], int len, int on) {
for(int i = 0; i < len; i++) if(i < R[i]) swap(y[i], y[R[i]]);
for(int i = 1; i < len; i <<= 1) {
int wn = on == -1 ? w2[i] : w1[i];
for(int j = 0; j < len; j += i << 1) {
int w = 1;
for(int k = 0; k < i; k++, w = (LL)w * wn % mod) {
LL u = y[j + k], v = (LL)y[j + k + i] * w % mod;
y[j + k] = add(u, v), y[j + k + i] = add(u, mod - v);
}
}
} if(on == -1) {
int cc = pow_mod(len, mod - 2);
for(int i = 0; i < len; i++) y[i] = (LL)y[i] * cc % mod;
}
}
int main() {
int n = read(), m = read(), S = read();
jc[0] = inv[0] = 1; for(int i = 1; i <= _max(n, m); i++) jc[i] = (LL)jc[i - 1] * i % mod;
inv[_max(n, m)] = pow_mod(jc[_max(n, m)], mod - 2); for(int i = _max(n, m) - 1; i >= 1; i--) inv[i] = (LL)inv[i + 1] * (i + 1) % mod;
for(int i = 0; i <= m; i++) w[i] = read();
int p = 1; f[0] = pow_mod(m, n);
for(int i = 1; i <= m; i++) {
p = (LL)p * C(n - (i - 1) * S, S) % mod;
f[i] = (LL)C(m, i) * p % mod * pow_mod(m - i, n - i * S) % mod;
f[i] = (LL)f[i] * jc[i] % mod;
} for(int i = 0; i <= m; i++) g[m - i] = i & 1 ? mod - inv[i] : inv[i];
int len = 1, L = 0;
for(; len <= m + m; len <<= 1);
for(int i = 0; i < len; i++) R[i] = (R[i >> 1] >> 1) | (i & 1) * (len >> 1);
pre(len);
NTT(f, len, 1), NTT(g, len, 1);
for(int i = 0; i < len; i++) f[i] = (LL)f[i] * g[i] % mod;
NTT(f, len, -1);
int ans = 0;
for(int i = m; i <= m + m; i++) ans = add(ans, (LL)f[i] * w[i - m] % mod * inv[i - m] % mod);
printf("%d\n", ans);
return 0;
}