前言
solve 4
rnk247
占了罚时的便宜。
CF不占罚时便宜就会被罚时占便宜
感觉这场似乎都是性质题,一眼看出性质就秒了,看不出就很难做出来了。
C似乎卡了很多人。但我做起来还好。
D2做不出来有些懊恼。
E是妙题。
题目
A
水题,保证male之间两两距离大于等于2即可。
然而一开考肚子就不舒服去了撤硕,回来网还卡。
过完已经差不多有2000人了。
B
样例奇数答案全是0,很有提示性。
发现当且仅当n为偶数时,奇偶配对gcd为2,其他时候都不合法。
那么答案显然就是
(
n
2
!
)
2
(\dfrac{n}{2}!)^2
(2n!)2。
由于A的影响和网络,通过还是一千多,当时已经感觉这场要凉…
C
当且仅当最大值在第一位的时候,答案是1。
然后每有一个数转到前面,答案最多+1,最少为2。
这是合法的必要条件,稍微想一下可以发现只要符合就可以类似链表插入的方法构造出来。
还好只需要判合法,不需要真的构造,不然其实挺麻烦的。
交完发现竟然只有100多了?大受震撼。
赛后看榜,似乎C确实卡了不少人,feedback 也有很多踩。
D1
位运算首先肯定是想按位考虑啦。
发现如果某一位1的总数和原来不一样,那肯定x的这一位是1.。
但是这个当这一位1的个数原来恰好为一半的时候会无法确定。
但是发现
l
=
0
l=0
l=0 的时候这种情况只会在
r
=
.
.
.
.
111
r=....111
r=....111 的低位这些1的位置出现。
这些位置显然
x
x
x 填不填1都行。
然后D1就做完了。
至此:
结束:
坐牢开始了…
D2
没了
l
=
0
l=0
l=0 的限制。
先抱着侥幸心理交了一发 D1 的码,发现果然是假的。
然后想了一些假做法,似乎都不太对。
但这题卡掉有些不应该,至少暴力
01
trie
01\text{trie}
01trie 判合法的做法应该不难想到的。
在CF就情不自禁开始想魔法,反而忘记了这些很OI的做法了。
题解的做法确实很巧妙。
由于给的数全是互异的,所以我只需要保证异或完的数全是
[
l
,
r
]
[l,r]
[l,r] 即可,它们必然形成一个互异的排列。
考虑性质:
a
⊕
b
=
1
→
(
a
⊕
x
)
⊕
(
b
⊕
x
)
=
1
a\oplus b=1\to (a\oplus x)\oplus(b\oplus x)=1
a⊕b=1→(a⊕x)⊕(b⊕x)=1。反过来也是成立的。
所以如果
l
l
l 为偶数,
r
r
r 为奇数,那么给出的数就会两两组成
x
⊕
y
=
1
x\oplus y=1
x⊕y=1 配对。
此时我最低位取
0
/
1
0/1
0/1 都可以,所以可以直接把范围缩小一半,递归的考虑。
直到不满足这个条件时,就必然会有无法配对的数。
它要么原来是
l
l
l,要么原来是
r
r
r,分别暴力check一下即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;
const int N=5e3+100;
const int mod=1e9+7;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
set<int>s1,s2;
int l,r;
void work(){
s1.clear();s2.clear();
l=read(),r=read();
for(int i=l;i<=r;i++){
s1.insert(read());
}
int mul(1);
while(l%2==0&&r%2==1){
mul<<=1;l>>=1;r>>=1;
s2.clear();
for(int i:s1){
s2.insert(i>>1);
}
swap(s1,s2);
}
//printf("mul=%d (%d %d)\n",mul,l,r);
for(int i:s1){
if(s1.find(i^1)==s1.end()){
int res=i^l,flag=1;
for(int j:s1){
int now=j^res;
flag&=(now>=l&&now<=r);
}
if(flag) printf("%d\n",(i^l)*mul);
else printf("%d\n",(i^r)*mul);
return;
}
}
assert(0);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
int T=read();
while(T--) work();
return 0;
}
/*
*/
E
关键性质:每一步放置位置的价值都必须比上一步大。
不然对方就可以接着在原来的位置摆烂了。
那么合法的转移就变成了一个 DAG。
考虑一个点必胜,当且仅当它能控制所有比它大的必胜点。
曼哈顿距离不好上数据结构(我记得很早的一次模拟我还真用一个魔法主席树嗯维护了),转成切比雪夫距离之后就变成判断是否一个点集的所有点都在特定矩形内,如果是把该点加入点集。
别tm树状数组了。
维护横纵坐标的
max
,
min
\max,\min
max,min 即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;
const int N=2e3+100;
const int mod=1e9+7;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,k;
int mxx,mnx,mxy,mny;
int u[N*N],v[N*N],a[N][N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();k=read();
mnx=mny=n+n+1;
mxx=mxy=-n-n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x=read();
u[x]=i;v[x]=j;
}
}
for(int i=n*n;i>=1;i--){
int x=u[i]+v[i],y=u[i]-v[i];
if(mnx>=x-k&&mxx<=x+k&&mny>=y-k&&mxy<=y+k){
//printf("%d\n",i);
a[u[i]][v[i]]=1;
mnx=min(mnx,x);
mxx=max(mxx,x);
mny=min(mny,y);
mxy=max(mxy,y);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
putchar(a[i][j]?'M':'G');
}
putchar('\n');
}
return 0;
}
/*
*/