[比赛题解] Codeforces Round 940 (Div. 2) and CodeCraft-23
比赛链接:Codeforces Round 940 (Div. 2) and CodeCraft-23 - Codeforces
C. How Does the Rook Move?
经过若干步合法步骤后,答案可以看作在空白的 n × n n\times n n×n棋盘上放置棋子。
考虑 n × n n\times n n×n棋盘上第一行被什么占据:
(1) 被 ( 1 , 1 ) (1,1) (1,1)的白棋占据,则剩余一个 ( n − 1 ) × ( n − 1 ) (n-1)\times(n-1) (n−1)×(n−1)棋盘。
(2) 被 ( 1 , i ) (1,i) (1,i)或 ( i , 1 ) (i,1) (i,1)的白棋占据,剩余一个 ( n − 2 ) × ( n − 2 ) (n-2)\times(n-2) (n−2)×(n−2)棋盘。
设方案数为 d p [ n ] dp[n] dp[n],得到dp方程 d p [ n ] = d p [ n − 1 ] + 2 ( i − 1 ) d p [ n − 2 ] dp[n]=dp[n-1]+2(i-1)dp[n-2] dp[n]=dp[n−1]+2(i−1)dp[n−2]。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 300000+5;
const int N = 300000;
const int mod = 1e9+7;
int T, n, k;
int dp[MAXN];
int main(){
dp[0] = dp[1] = 1;
for(int i = 2; i <= N; i++){
dp[i] = (dp[i-1] + (ll)dp[i-2]*2*(i-1)) % mod;
}
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &k);
while(k--){
int x, y;
scanf("%d%d", &x, &y);
if(x == y) n--;
else n -= 2;
}
printf("%d\n", dp[n]);
}
return 0;
}
D. A BIT of an Inequality
按二进制位考虑,若有 f ( x , y ) ⊕ f ( y , z ) > f ( x , z ) f(x,y)\oplus f(y,z)>f(x,z) f(x,y)⊕f(y,z)>f(x,z),则在 a y a_y ay的最高二进制位上, a x ⊕ a x + 1 ⊕ ⋯ ⊕ a z a_x\oplus a_{x+1}\oplus\dots\oplus a_z ax⊕ax+1⊕⋯⊕az 在该位为 0 0 0。即在该二进制位上, a [ x , y ) a[x,y) a[x,y)内1的个数加 a ( y , z ] a(y,z] a(y,z]内1的个数为奇数,统计每个二进制位上使得 a [ i , y ) a[i,y) a[i,y)内1的个数为奇数和使得 a ( y , i ] a(y,i] a(y,i]内1的个数为奇数的 i i i的个数,即可计算答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100000+5;
int T, n;
int a[MAXN], cl[40], cr[40], cnt[40];
ll ans;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for(int i = 0; i <= 30; i++){
cnt[i] = 0;
cl[i] = cr[i] = 0;
}
for(int i = 2; i <= n; i++){
for(int k = 0; k <= 30; k++){
cnt[k] += (a[i] >> k) & 1;
if(cnt[k] & 1)
cr[k]++;
}
}
ans = 0;
for(int i = 1; i <= n; i++){
for(int k = 30; k >= 0; k--){
if((a[i] >> k) & 1){
// printf("i = %d k = %d\n", i, k);
// printf("cl %lld cr %lld\n", cl[k], cr[k]);
ans += (ll)cl[k] * (n-i-cr[k]+1);
ans += (ll)(i-1-cl[k]+1) * cr[k];
break;
}
}
for(int k = 0; k <= 30; k++){
if((a[i] >> k) & 1){
cl[k] = i - cl[k];
}
if((a[i+1] >> k) & 1){
cr[k] = n-i-cr[k];
}
}
}
printf("%lld\n", ans);
}
return 0;
}
E. Carousel of Combinations
C ( i , j ) = i ! j ( i − j ) ! mod j C(i,j)=\frac{i!}{j(i-j)!} \ \text{mod}\ j C(i,j)=j(i−j)!i! mod j
打表找规律+线段树/树状数组维护差分数组后计算答案
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1000000+5;
const int N = 1000000;
const ll mod = 1e9+7;
int T;
ll n;
int len[MAXN<<2];
ll ans[MAXN], sum[MAXN<<2], tag[MAXN<<2];
int isp(int n){
if(n <= 1) return 0;
for(int i = 2; i*i <= n; i++)
if(n%i == 0) return 0;
return 1;
}
void build(int x, int l, int r){
len[x] = r-l+1;
if(l == r) return;
int mid = (l+r)/ 2;
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
}
void push_up(int x){
sum[x] = sum[x<<1] + sum[x<<1|1];
}
void push_down(int x, int l, int r){
if(tag[x]){
int mid = (l+r)/2, ls = x<<1, rs = x<<1|1;
tag[ls] = tag[ls] + tag[x];
sum[ls] = sum[ls] + tag[x]*len[ls];
tag[rs] = tag[rs] + tag[x];
sum[rs] = sum[rs] + tag[x]*len[rs];
tag[x] = 0;
}
}
void add(int x, int l, int r, int L, int R, ll c){
if(L <= l && r <= R){
sum[x] = sum[x] + c*len[x];
tag[x] = tag[x] + c;
return;
}
push_down(x, l, r);
int mid = (l+r)/2;
if(R <= mid) add(x<<1, l, mid, L, R, c);
else if(L > mid) add(x<<1|1, mid+1, r, L, R, c);
else{
add(x<<1, l, mid, L, mid, c);
add(x<<1|1, mid+1, r, mid+1, R, c);
}
push_up(x);
}
ll query(int x, int l, int r, int L, int R){
if(L <= l && r <= R) return sum[x];
push_down(x, l, r);
int mid = (l+r) / 2;
if(R <= mid) return query(x<<1, l, mid, L, R);
if(L > mid) return query(x<<1|1, mid+1, r, L, R);
return query(x<<1, l, mid, L, mid) + query(x<<1|1, mid+1, r, mid+1, R);
}
int main(){
build(1, 1, N);
for(int i = 2; i <= N; i++){
if(isp(i)){
int x = i-1;
for(int l = i, r = min(N, l+i-1); l <= N; l = r+1, r = min(N, l+i-1)){
// printf("lrx %d %d %lld\n", l, r, x);
add(1, 1, N, l, r, x);
x = (x-1+i) % i;
}
}
}
int x = 2;
for(int l = 4, r = min(N, l+4-1); l <= N; l = r+1, r = min(N, l+4-1)){
add(1, 1, N, l, r, x);
x ^= 2;
}
for(int i = 1; i <= N; i++){
ans[i] = query(1, 1, N, 1, i) % mod;
}
scanf("%d", &T);
while(T--){
scanf("%lld", &n);
printf("%lld\n", ans[n]);
}
return 0;
}
F2. Frequency Mismatch (Hard Version)
对于树上每个节点 u u u维护一个表示 ( 1 , u ) (1,u) (1,u)路径上经过点集的权值线段树,在线段树的每个节点上保存区间上有关频率的哈希值。比较两条路径上点构成的可重集是否相同,就是比较不同线段树区间的哈希值。设 f ( u ) f(u) f(u)是 ( 1 , u ) (1,u) (1,u)路径上经过点集的权值线段树,则路径 ( u , v ) (u,v) (u,v)的信息由 f ( u ) + f ( v ) − f ( l c a ) − f ( f a [ l c a ] ) f(u)+f(v)-f(lca)-f(fa[lca]) f(u)+f(v)−f(lca)−f(fa[lca])得到。用线段树方法可以找到两条路径在 [ 1 , 100000 ] [1,100000] [1,100000]内出现次数不同的最小值 C C C,下次在 [ C + 1 , 100000 ] [C+1,100000] [C+1,100000]内寻找下一个。
#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 100000+5;
const int N = 100000;
const int mod1 = 1e9+7;
const int mod2 = 1e9+9;
int n, tot, q;
int a[MAXN], fa[MAXN][17], dep[MAXN];
int rt[MAXN], ls[MAXN<<5], rs[MAXN<<5];
mt19937 rnd(time(0));
pii base, val[MAXN<<5], pw[MAXN];
vector<int> G[MAXN];
int u1, v1, u2, v2, c1, c2;
pii operator + (const pii &a, const pii &b){
int c1 = a.fi + b.fi, c2 = a.se + b.se;
if(c1 >= mod1) c1 -= mod1;
if(c2 >= mod2) c2 -= mod2;
return mp(c1, c2);
}
pii operator - (const pii &a, const pii &b){
int c1 = a.fi - b.fi, c2 = a.se - b.se;
if(c1 < 0) c1 += mod1;
if(c2 < 0) c2 += mod2;
return mp(c1, c2);
}
pii operator * (const pii &a, const pii &b){
return mp(1ll*a.fi*b.fi%mod1, 1ll*a.se*b.se%mod2);
}
void init_hash(int lim = 0){
base = {rnd()%mod1, rnd()%mod2};
pw[0] = mp(1, 1);
for(int i = 1; i <= lim; i++) pw[i] = pw[i-1] * base;
}
void push_up(int dir){
val[dir] = val[ls[dir]] + val[rs[dir]];
}
int update(int pos, int l, int r, int root){
int dir = ++tot;
ls[dir] = ls[root];
rs[dir] = rs[root];
if(l == r){
val[dir] = val[root] + pw[l];
// printf("l = %d %d %d %d\n", l, dir, val[dir].fi, val[dir].se);
return dir;
}
int mid = (l+r) / 2;
if(pos <= mid)
ls[dir] = update(pos, l, mid, ls[dir]);
else
rs[dir] = update(pos, mid+1, r, rs[dir]);
push_up(dir);
return dir;
}
void dfs(int u, int f){
fa[u][0] = f;
dep[u] = dep[f] + 1;
rt[u] = update(a[u], 1, N, rt[f]);
for(int v : G[u]){
if(v == f) continue;
dfs(v, u);
}
}
int getlca(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
for(int i = 0; i <= 16; i++){
if(((dep[u] - dep[v]) >> i) & 1){
u = fa[u][i];
}
}
if(u == v) return u;
for(int i = 16; i >= 0; i--){
if(fa[u][i] != fa[v][i]){
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
pii calc(int u1, int u2, int u3, int u4){
pii tmp = val[u1] + val[u2] - val[u3] - val[u4];
return val[u1] + val[u2] - val[u3] - val[u4];
}
int find(int u1, int u2, int u3, int u4, int v1, int v2, int v3, int v4, int l, int r, int L, int R){
// printf("%d %d %d %d %d %d %d %d %d %d %d %d\n", u1, u2, u3, u4, v1, v2, v3, v4, l, r, L, R);
if(l == r){
pii h1 = calc(u1, u2, u3, u4);
pii h2 = calc(v1, v2, v3, v4);
return h1 == h2 ? N+1 : l;
}
int mid = (l+r) / 2;
if(L <= l && r <= R){
pii h1 = calc(ls[u1], ls[u2], ls[u3], ls[u4]);
pii h2 = calc(ls[v1], ls[v2], ls[v3], ls[v4]);
if(h1 == h2) return find(rs[u1], rs[u2], rs[u3], rs[u4], rs[v1], rs[v2], rs[v3], rs[v4], mid+1, r, mid+1, R);
else return find(ls[u1], ls[u2], ls[u3], ls[u4], ls[v1], ls[v2], ls[v3], ls[v4], l, mid, L, mid);
}
if(R <= mid) return find(ls[u1], ls[u2], ls[u3], ls[u4], ls[v1], ls[v2], ls[v3], ls[v4], l, mid, L, R);
if(L > mid) return find(rs[u1], rs[u2], rs[u3], rs[u4], rs[v1], rs[v2], rs[v3], rs[v4], mid+1, r, L, R);
return min(find(ls[u1], ls[u2], ls[u3], ls[u4], ls[v1], ls[v2], ls[v3], ls[v4], l, mid, L, mid), find(rs[u1], rs[u2], rs[u3], rs[u4], rs[v1], rs[v2], rs[v3], rs[v4], mid+1, r, mid+1, R));
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for(int i = 1; i < n; i++){
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
init_hash(N);
dfs(1, 0);
// for(int i = 1; i <= n; i++){
// printf("%d %d %d %d\n", i, rt[i], ls[rt[i]], rs[rt[i]]);
// }
for(int j = 1; j <= 16; j++){
for(int i = 1; i <= n; i++){
fa[i][j] = fa[fa[i][j-1]][j-1];
}
}
scanf("%d", &q);
while(q--){
int k;
scanf("%d%d%d%d%d", &u1, &v1, &u2, &v2, &k);
c1 = getlca(u1, v1);
c2 = getlca(u2, v2);
// printf("%d %d lca %d\n", u1, v1, c1);
// printf("%d %d lca %d\n", u2, v2, c2);
int C = 1;
vector<int> ans;
while(k > 0 && C <= N){
// printf("l = %d\n", C);
C = find(rt[u1], rt[v1], rt[c1], rt[fa[c1][0]], rt[u2], rt[v2], rt[c2], rt[fa[c2][0]], 1, N, C, N);
k--;
if(C <= N) ans.push_back(C);
C++;
}
printf("%d ", ans.size());
for(int p : ans)
printf("%d ", p);
putchar('\n');
}
return 0;
}
Codeforces Round 940 (Div 2) 比赛题解
3085

被折叠的 条评论
为什么被折叠?



