%%% FSYo Orz orz orz
F
考完了之后才发现是 Trie 树或者 Hash 表的模板题(我太蒻了)。考试的时候打了一个 O ( n n ! ) O(nn!) O(nn!) 的 dfs 暴力:
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2020
#define endl '\n'
int n = 0;
int a[MAXN] = { 0 };
int b[MAXN] = { 0 };
int c[MAXN] = { 0 };
int p[MAXN] = { 0 };
int x[MAXN] = { 0 };
int vis[MAXN] = { 0 };
bool comp(int x, int y){
return x < y;
}
int cnt = 0;
int ans[MAXN] = { 0 };
void work(){
for(int i = 1; i <= n; i++){
x[i] = a[i] ^ b[p[i]];
if(i > 1 and x[i] != x[i-1]) return;
}
ans[++cnt] = x[1];
}
void dfs(int now){
if(now > n){
work(); return;
}
for(int i = 1; i <= n; i++)
if(!vis[c[i]]){
vis[c[i]] = 1; p[now] = c[i];
dfs(now + 1);
vis[c[i]] = 0; p[now] = 0;
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
for(int i = 1; i <= n; i++) c[i] = i;
dfs(1);
sort(ans+1, ans+cnt+1, comp); cout << cnt << endl;
for(int i = 1; i <= cnt; i++) cout << ans[i] << ' ';
puts("");
return 0;
}
考完之后发现 x x x 的取值就只有 n 2 n^2 n2 中可能,所以我们 O ( n 2 ) O(n^2) O(n2) 枚举所有 ( a i , b j ) (a_i,b_j) (ai,bj),并把它们和它们的异或值存进一个 Hash 表里面,然后就再遍历一遍 Hash 表,答案就是出现次数 ≥ n \geq n ≥n 次的所有 x x x(这么简单我竟然没做出来…)。
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 2020
#define endl '\n'
inline int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int n = 0;
int a[MAXN] = { 0 };
int b[MAXN] = { 0 };
unordered_map<int , int> s;
int cnt = 0;
int ans[MAXN] = { 0 };
bool comp(int x, int y){
return x < y;
}
void check(int x){
unordered_map <int, int> c;
for(int i = 1; i <= n; i++) c[b[i]]++;
for(int i = 1; i <= n; i++){
if(!c[a[i] ^ x]) return;
c[a[i] ^ x]--;
}
ans[++cnt] = x;
}
int main(){
n = in;
for(int i = 1; i <= n; i++) a[i] = in;
for(int i = 1; i <= n; i++) b[i] = in;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
s[a[i] ^ b[j]]++;
for(auto x : s)
if(x.second >= n) check(x.first);
sort(ans+1, ans+cnt+1, comp);
cout << cnt << endl;
for(int i = 1; i <= cnt; i++)
cout << ans[i] << ' ';
puts("");
return 0;
}
这里的 unordered_map 不像 map 一样是用红黑树实现,这个可以实现 O ( 1 ) O(1) O(1) 下标查询,所以就能 O ( n 2 ) O(n^2) O(n2) 过掉。
S
一道神奇的构造题,构造方式好想但是证明就不太好想了(考场上不证明打了正解 qwq)。
我是这样构造的:
显然这样是最优的qwq. 我们就来证明一下这样构造是最优的。首先我们看上界是多少。这个图主要的限制就是一组周围的一圈都不能有点,所以我们考虑两种情况:
周围一圈红色的就是不能填的,我们要让能填的最大,那么肯定选择竖着的或者横着的而不是斜着放的。然后可以证到上界就是
⌊
(
n
+
1
)
2
6
⌋
−
2
\lfloor\frac{(n+1)^2}{6}\rfloor-2
⌊6(n+1)2⌋−2,我们把
n
n
n 分为三种种情况计算发现这种排法和上界都是一样的。然后这种构造方法就是最优的了。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1010
#define endl '\n'
typedef long long ll;
int n = 0;
int type = 0;
int f[MAXN] = { 0 };
int ans[MAXN][MAXN] = { 0 };
int main(){
scanf("%d%d", &n, &type);
if(!type){
if(n <= 1000){
f[2] = 2; f[4] = 8; f[6] = 16;
for(int i = 8; i <= n; i += 2) f[i] = f[i-6] + 4 * (i - 2);
cout << f[n] << endl;
}
else{
ll ans = 0;
if((n - 2) % 6 == 0) ans = 2 + (1ll * n * n + 1ll * 2 * n - 8) / 3;
else if((n - 4) % 6 == 0) ans = 8 + (1ll * n * n + 1ll * 2 * n - 24) / 3;
else ans = 16 + (1ll * n * n + 1ll * 2 * n - 48) / 3;
cout << ans << endl;
}
return 0;
}
else{
f[2] = 2; f[4] = 8; f[6] = 16;
for(int i = 8; i <= n; i += 2) f[i] = f[i-6] + 4 * (i - 2);
cout << f[n] << endl;
if(n == 2) ans[1][1] = ans[2][2] = 1;
else if(n == 4){
ans[1][1] = ans[1][2] = ans[1][4] = 1;
ans[2][4] = ans[3][1] = 1;
ans[4][1] = ans[4][3] = ans[4][4] = 1;
}
else if(n == 6){
ans[1][1] = ans[1][2] = ans[1][4] = ans[1][6] = 1;
ans[2][4] = ans[2][6] = ans[5][1] = ans[5][3] = 1;
ans[3][1] = ans[3][2] = ans[4][5] = ans[4][6] = 1;
ans[6][1] = ans[6][3] = ans[6][5] = ans[6][6] = 1;
}
else{
for(int x = 1; x <= n / 2; x += 3){
for(int i = x; i <= n - x - 1 ; i += 2){
ans[i][x] = ans[i][x+1] = 1;
ans[n-x+1][i] = ans[n-x][i] = 1;
}
for(int i = x + 3; i <= n - x + 1 ; i += 2){
ans[x][i] = ans[x+1][i] = 1;
ans[i][n-x+1] = ans[i][n-x] = 1;
}
}
if((n - 8) % 6 == 0) ans[n/2][n/2] = ans[n/2+1][n/2+1] = 1;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout << ans[i][j] << ' ';
}
puts("");
}
}
return 0;
}
Y
让 c i c_i ci 是第 i i i 个人向后传的球的个数。
当 min i = 1 n c i ≠ 0 \min\limits_{i=1}^nc_i \neq 0 i=1minnci=0 的时候,我们发现如果我们把所有的 c i c_i ci 都减 1,是不会影响交换之后产生的序列的。所以我们可以把每个 c i c_i ci 都减去一个 min i = 1 n c i \min\limits_{i=1}^nc_i i=1minnci 使得 min i = 1 n c i = 0 \min\limits_{i=1}^nc_i = 0 i=1minnci=0,然后再进行处理(下文中默认 min i = 1 n c i = 0 \min\limits_{i=1}^nc_i = 0 i=1minnci=0)。
我们知道,我们一共有
t
o
t
=
∏
i
=
1
n
(
a
i
+
1
)
−
∏
i
=
1
n
a
i
tot = \prod\limits_{i=1}^n (a_i+1) - \prod\limits_{i=1}^n a_i
tot=i=1∏n(ai+1)−i=1∏nai 种这样的序列
c
c
c (解释一下:没有
min
i
=
1
n
c
i
≠
0
\min\limits_{i=1}^nc_i \neq 0
i=1minnci=0 这个条件的时候,第
i
i
i 个人能给别人的球的个数为
a
i
+
1
a_i + 1
ai+1,所以所有的可能就是
∏
i
=
1
n
(
a
i
+
1
)
\prod\limits_{i=1}^n (a_i+1)
i=1∏n(ai+1),如果要求
∀
c
i
>
0
\forall c_i > 0
∀ci>0 那么所有的可能就是
∏
i
=
1
n
a
i
\prod\limits_{i=1}^n a_i
i=1∏nai。然后两式相减就得到了条件
min
i
=
1
n
c
i
≠
0
\min\limits_{i=1}^nc_i \neq 0
i=1minnci=0 成立时的总个数) 。所以如果我们能找到的
c
i
c_i
ci 序列所形成的结果序列
x
i
x_i
xi 那我们就找到了答案就是
∑
i
=
1
t
o
t
∏
j
=
1
n
x
i
,
j
\sum\limits_{i=1}^{tot}\prod\limits_{j=1}^n x_{i,j}
i=1∑totj=1∏nxi,j。我们也知道
∏
i
=
1
n
x
i
\prod\limits_{i=1}^n x_i
i=1∏nxi 的值代表的是某一个
c
c
c 序列操作后的结果序列
x
x
x 的各个项的乘积,但是它也可以代表 第
i
i
i 个人有
x
i
x_i
xi 个球,每次从一个人手里拿一个球的取法的方案数。
然后后面的 DP 我就看不懂了(我太蒻了)…
o
有生之年第一次在考场上打了 T4,太感动了。一看题目,wow,裸的线段树能拿 20 分!
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define MAXN 50500
#define ls(p) (p << 1)
#define rs(P) (p << 1 | 1)
#define in read()
#define endl '\n'
inline int read(){
int x = 0; char c = getchar();
while(c > '9' or c < '0') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int a[MAXN] = { 0 };
int n = 0; int q = 0;
struct Tnode{
int l, r;
int dat;
}t[4 * MAXN];
inline void pushup(int p){
t[p].dat = t[ls(p)].dat + t[rs(p)].dat;
}
void build(int p, int l, int r){
t[p].l = l; t[p].r = r;
if(l == r){
t[p].dat = a[l]; return;
}
int mid = (l + r) >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
void update(int p, int index, int val){
if(t[p].l == t[p].r){
t[p].dat = val; return;
}
int mid = (t[p].l + t[p].r) >> 1;
if(index <= mid) update(ls(p), index, val);
else update(rs(p), index, val);
pushup(p);
}
int query(int p, int l, int r){
if(l <= t[p].l and t[p].r <= r) return t[p].dat;
int mid = (t[p].l + t[p].r) >> 1; int ans = 0;
if(l <= mid) ans += query(ls(p), l, r);
if(r > mid) ans += query(rs(p), l, r);
return ans;
}
struct Tqu{
int t;
int idx;
int ans;
int l, r;
}qu[MAXN];
bool comp1(Tqu a, Tqu b){
return a.t < b.t;
}
bool comp2(Tqu a, Tqu b){
return a.idx < b.idx;
}
signed main(){
n = in; q = in;
for(int i = 1; i <= n; i++) a[i] = in;
build(1, 1, n);
for(int i = 1; i <= q; i++){
qu[i].t = in; qu[i].l = in; qu[i].r = in; qu[i].idx = i;
} sort(qu+1, qu+q+1, comp1);
int idx = 1;
for(int i = 1; i <= qu[q].t; i++){
for(int j = 1; j <= n; j++) update(1, j, max(a[j], a[j-1]));
for(int j = 1; j <= n; j++) a[j] = query(1, j, j);
while(qu[idx].t == i){
qu[idx].ans = query(1, qu[idx].l, qu[idx].r);
idx++;
}
} sort(qu+1, qu+q+1, comp2);
for(int i = 1; i <= n; i++) cout << qu[i].ans << endl;
return 0;
}