NOI Oline 2题解
A
假设 p1 < p2
对于lcm(p1, p2)肯定是选择涂成 p2的颜色,因为左右最近的都是p1颜色的
考虑最差的情况, 两个p2颜色的格子塞了最多的p1颜色。
两个p2颜色中间有p2-1个格子
最差的情况就是这p2-1个格子中第一个被染了p1颜色,然后每隔p1个染p1色
也就是
p 2 − 1 − 1 p 1 + 1 \frac{p_2-1 -1}{p1} + 1 p1p2−1−1+1
和k比较一下就好了
注意k=1的情况
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t, p1, p2, k;
signed main() {
scanf("%lld", &t);
while(t --) {
scanf("%lld%lld%lld", &p1, &p2, &k);
if(p1 > p2) swap(p1, p2);
int d = __gcd(p1, p2);
p1 /= d, p2 /= d;
if(k == 1 || (p2 > 2 && (p2 - 2) / p1 + 1 >= k)) printf("NO\n");
else printf("YES\n");
}
return 0;
}
B
没啥好说的呀
根据套路
设
g
(
r
)
=
∑
l
=
1
r
f
(
l
,
r
)
g(r) = \sum\limits_{l=1}^r f(l,r)
g(r)=l=1∑rf(l,r)
A
N
S
=
∑
r
=
1
n
g
(
r
)
ANS = \sum\limits_{r=1}^ng(r)
ANS=r=1∑ng(r)
像这种求集合大小的只需要考虑每个数对那些集合有贡献就行了
记个 pre[i]表示上一个和 i 相同的位置
动态维护f(l, r)
考虑从g( r - 1)转移到 g( r )
发现r只对l在[pre[r] + 1, r]有贡献
即 对于l在这个区间内的 f ( l, r) = f(l, r - 1) + 1
平方一下
然后和原始相减就可以发现只用求 一段区间f(l, r - 1)
即需要一个数据结构维护区间加,区间求和
线段树好像会被卡常,用树状数组就好了
code:
#include<bits/stdc++.h>
#define mod 1000000007
#define N 1000005
#define int long long
#define lowbit(x) (x & -x)
using namespace std;
inline void MOD(int &x) {
if(x < 0) x += mod;
if(x >= mod) x -= mod;
}
int tree1[N + 5], tree2[N + 5];
void update1(int x, int y) {
for(; x < N; x += lowbit(x)) tree1[x] += y, MOD(tree1[x]);
}
int query1(int x) {
int ret = 0;
for(; x; x -= lowbit(x)) ret += tree1[x], MOD(ret);
return ret;
}
void update2(int x, int y) {
for(; x < N; x += lowbit(x)) tree2[x] += y, MOD(tree2[x]);
}
int query2(int x) {
int ret = 0;
for(; x; x -= lowbit(x)) ret += tree2[x], MOD(ret);
return ret;
}
void add(int l, int r, int o) {
update1(l, o % mod), update1(r + 1, - o % mod);
update2(l, o * l % mod), update2(r + 1, - o * (r + 1) % mod);
}
int query(int x) {
return ((x + 1) * query1(x) % mod - query2(x) + mod) % mod;
}
int n, a[N], b[N], mp[N], pre[N];
signed main() {
scanf("%lld", &n);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]), b[i] = a[i];
sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; i ++) {
int pos = lower_bound(b + 1, b + 1 + n, a[i]) - b;
pre[i] = mp[pos];
mp[pos] = i;
}
//for(int i = 1; i <= n; i ++) printf("%d ", pre[i]); printf("\n");
int last = 0, ans = 0;
for(int r = 1; r <= n; r ++) {
last += 2 * (query(r) - query(pre[r]) + mod) % mod + r - pre[r], last %= mod;
ans += last, ans %= mod;
add(pre[r] + 1, r, 1);
}
printf("%lld", ans);
return 0;
}
C
首先要知道二项式反演
根据之前做多项式的套路,可以设 g(i)为恰好i次非平局, f(i)为钦定i次非平局
然后f(i)可以直接树形DP出来,再根据二项式定理就可以算出 g(i)了
答案就是
∑
g
(
i
)
\sum g(i)
∑g(i)
code:
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define N 10005
using namespace std;
int fac[N], ifac[N];
int qpow(int x, int y) {
int ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
int C(int x, int y) {
return fac[x] * ifac[x - y] % mod * ifac[y] % mod;
}
struct edge {
int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
int size[N], sza[N], dp[N][N], a[N], ls[N], n, f[N];
void dfs(int u, int fa) {
size[u] = 1, sza[u] = a[u];
dp[u][0] = 1;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dfs(v, u);
for(int j = 0; j <= size[u] + size[v]; j ++) ls[j] = 0;
for(int j = 0; j <= size[u] / 2; j ++)
for(int k = 0; k <= size[v] / 2; k ++)
ls[j + k] += dp[u][j] * dp[v][k] % mod, ls[j + k] %= mod;
for(int j = 0; j <= size[u] + size[v]; j ++) dp[u][j] = ls[j];
size[u] += size[v], sza[u] += sza[v];
}
for(int i = min(sza[u], size[u] - sza[u]); i >= 1; i --)
dp[u][i] += dp[u][i - 1] * (a[u]? (size[u] - sza[u] - (i - 1)) : (sza[u] - (i - 1))) % mod, dp[u][i] %= mod;
}
signed main() {
init();
scanf("%lld", &n);
ifac[0] = fac[0] = 1;
for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i >= 1; i --) ifac[i] = ifac[i + 1] * (i + 1) % mod;
for(int i = 1; i <= n; i ++) {
char ch;
scanf(" %c", &ch);
if(ch == '0') a[i] = 0;
else a[i] = 1;
}
for(int i = 1; i < n; i ++) {
int u, v;
scanf("%lld%lld", &u, &v);
insert(u, v);
insert(v, u);
}
dfs(1, 1);
for(int i = 0; i <= n / 2; i ++) f[i] = dp[1][i] * fac[n / 2 - i] % mod;
for(int i = 0; i <= n / 2; i ++) {
int ret = 0;
for(int j = i; j <= n / 2; j ++)
ret += C(j, i) * ((j - i & 1) ? mod - 1 : 1) % mod * f[j] % mod + mod, ret %= mod;
printf("%lld\n", ret);
}
return 0;
}