前言
没脑子又被吊打了,生气(T2签到题写了2.5h)。
赶紧订正。
A
不妨设(p_1<p_2),两种颜色对应(1,2)。容易发现既是(p_1)倍数有是(p_2)倍数的点肯定染成(2),因为前后都是(1)。由于(>lcm)的情况和([1, lcm])一样,只需考虑([1,lcm])。由于(1)十分密集,影响答案的段一定是连续的(1)。
于是我们得找到(y p_2 < x p_1 < (x + k - 1) p_1 < (y+1) p_2),其中(xp_1-yp_2)得尽量小,(k)是极大值。
根据裴蜀定理,(xp_1-yp_2)最小值是(gcd(p_1,p_2))。
最长连续段长度就是(dfrac{p_2 - 1 - gcd(p_1, p_2)}{p_1} + 1)
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 1e6 + 50, mod = 1e9 + 7;
int p1, p2, k;
int gcd(int a, int b) {
return !b ? a : gcd(b, a % b);
}
int main() {
// freopen("color.in", "r", stdin);
// freopen("color.out", "w", stdout);
int t; scanf("%d", &t);
while(t --) {
scanf("%d%d%d", &p1, &p2, &k);
if(p1 > p2) swap(p1, p2);
if(k == 1) puts("No");
else if(p1 == p2) puts("Yes");
else {
int g = gcd(p1, p2);
puts((p2 - 1 - g) / p1 + 1 < k ? "Yes" : "No");
}
}
fclose(stdin); fclose(stdout);
return 0;
}
B
注意到((x+1)^2=x^2+(2x+1)),我们对于每个元素都考虑由他产生的(2x+1)对答案的贡献。
具体地,我们令(p_i)表示(a_i)上一次出现的位置(没有出现等于(0)),(nxt_i)表示下一次出现的位置(没有出现等于(n+1)),那对于位置(u),我们把答案加上(sum_{i = p_u}^u (2 c(i,u) - 1)(n-u+1)),其中(c(l,r))表示([l,r])中不同数的个数。我们记(q(p_u,u)=(n-u+1)sum_{i = p_u}^u c(i,u)),考虑求这个(q)。
然后考虑每个位置对这些(q)询问的贡献。对于位置(u),他对(q(l,r))有贡献,当且仅当(l leq u, uleq r < nxt_u),并且贡献是((n-r+1)(u + 1 - l))。拆一下就是((u + 1)(n - r + 1) - l (n-r+1)),那我们把询问按(l)排序(你也可以通过nxt数组来免去排序),然后(u=1 ightarrow n),把(lleq u)的(q)加入(2)个树状数组的(r)位置,这两个树状数组第一个维护(n-r+1),第二个维护(l(n-r+1))。然后对于每个(u)通过在树状数组上询问([u,nxt_u))把贡献加进去就行。
#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>
#define pb push_back
using namespace std;
typedef long long ll;
const int N = 1e6 + 50, mod = 1e9 + 7;
void upd(int &x, const int &y) {
(x += y) >= mod ? x -= mod : 0;
}
int n, ans1, ans2, a[N], pre[N], p[N], nxt[N];
struct uni {
int a[N];
void build(int *b, int n) {
for(int i = 1; i <= n; i ++) a[i] = b[i];
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i ++) {
b[i] = lower_bound(a + 1, a + n + 1, b[i]) - a;
}
}
} uq;
struct Bit {
int bit[N];
void add(int u, int val) {
for(; u <= n; u += u & (-u)) {
upd(bit[u], val);
}
}
int qry(int u) {
int ans = 0;
for(; u >= 1; u &= u - 1) {
upd(ans, bit[u]);
}
return ans;
}
int qry(int l, int r) {
return (qry(r) - qry(l - 1) + mod) % mod;
}
} b0, b1;
struct Node {
int l, r;
bool operator < (const Node &b) const {
return l < b.l;
}
} an[N];
int main() {
// freopen("sequence.in", "r", stdin);
// freopen("sequence.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%d", a + i);
}
uq.build(a, n);
for(int i = 1; i <= n; i ++) {
p[i] = pre[a[i]]; pre[a[i]] = i;
upd(ans1, (i - p[i]) * (n - i + 1ll) % mod);
}
fill(pre + 1, pre + n + 1, n + 1);
for(int i = n; i >= 1; i --) {
nxt[i] = pre[a[i]]; pre[a[i]] = i;
an[i] = (Node) {p[i] + 1, i};
}
sort(an + 1, an + n + 1);
int ans3 = 0, cur = 0;
for(int i = 1; i <= n; i ++) {
while(cur < n && an[cur + 1].l <= i) {
cur ++;
b0.add(an[cur].r, n - an[cur].r + 1);
b1.add(an[cur].r, (n - an[cur].r + 1ll) * an[cur].l % mod);
}
upd(ans2, ((i + 1ll) * b0.qry(i, nxt[i] - 1) % mod - b1.qry(i, nxt[i] - 1) + mod) % mod);
}
ans1 = ((2ll * ans2 - ans1) % mod + mod) % mod;
printf("%d
", ans1);
fclose(stdin); fclose(stdout);
return 0;
}
C
我们用(f(u))表示标记(u)对祖先 - 后代关系结点,剩下(m-u)对随便配对的方案。(g(u))表示恰好有(u)对祖先 - 后代的方案数,题目要求(g(0))到(g(m))。
根据二项式反演,
这个(f(n))很好求,(dp[u][x])表示(u)子树标记(x)个祖先 - 后代关系的方案数(没标记的最后考虑,把阶乘乘上去)。先把子树背包合并,然后考虑选(u)。
#include <algorithm>
#include <cstdio>
#include <vector>
#define pb push_back
using namespace std;
typedef long long ll;
const int N = 5100, mod = 998244353;
int n, ans, fac[N], fav[N], dp[N][N], sz[2][N];
char s[N];
vector<int> G[N];
int C(int n, int m) {
return 1ll * fac[n] * fav[m] % mod * fav[n - m] % mod;
}
int qpow(int a, int b) {
int ans = 1;
for(; b >= 1; b >>= 1, a = (ll) a * a % mod)
if(b & 1) ans = (ll) ans * a % mod;
return ans;
}
#define full(u) min(sz[0][u], sz[1][u])
void dfs(int u, int fa = 0) {
sz[0][u] = s[u] == 0; sz[1][u] = s[u] == 1; dp[u][0] = 1;
for(int i = 0; i < (int) G[u].size(); i ++) {
int v = G[u][i];
if(v != fa) {
dfs(v, u);
static int t[N];
fill(t, t + full(u) + full(v) + 1, 0);
for(int j = full(u); j >= 0; j --) if(dp[u][j]) {
for(int k = full(v); k >= 0; k --) if(dp[v][k]) {
(t[j + k] += 1ll * dp[u][j] * dp[v][k] % mod) %= mod;
}
}
copy(t, t + full(u) + full(v) + 1, dp[u]);
sz[0][u] += sz[0][v]; sz[1][u] += sz[1][v];
}
}
for(int i = full(u) - 1; i >= 0; i --) {
(dp[u][i + 1] += 1ll * dp[u][i] * (sz[s[u] ^ 1][u] - i) % mod) %= mod;
}
}
int main() {
// freopen("match.in", "r", stdin);
// freopen("match.out", "w", stdout);
scanf("%d%s", &n, s + 1); int m = n >> 1;
for(int i = 1; i <= n; i ++) s[i] -= '0';
fac[0] = 1;
for(int i = 1; i <= m; i ++) fac[i] = 1ll * fac[i - 1] * i % mod;
fav[m] = qpow(fac[m], mod - 2);
for(int i = m; i >= 1; i --) fav[i - 1] = 1ll * fav[i] * i % mod;
for(int i = 1; i < n; i ++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].pb(v); G[v].pb(u);
}
dfs(1);
for(int i = 0; i <= m; i ++) {
dp[1][i] = 1ll * dp[1][i] * fac[m - i] % mod;
}
for(int i = 0; i <= m; i ++) {
int res = 0;
for(int j = i; j <= m; j ++) {
if((j - i) & 1) (res += mod - 1ll * dp[1][j] * C(j, i) % mod) %= mod;
else (res += 1ll * dp[1][j] * C(j, i) % mod) %= mod;
}
printf("%d
", res);
}
fclose(stdin); fclose(stdout);
return 0;
}