Codeforces Round #804 (Div. 2)思维题训练
Codeforces Round #804 (Div. 2) A. The Third Three Number Problem
原题指路:https://codeforces.com/contest/1699/problem/A
题意
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据给定一个 n ( 1 ≤ n ≤ 1 e 9 ) n\ \ (1\leq n\leq 1\mathrm{e}9) n (1≤n≤1e9),求正整数 a , b , c s . t . ( a x o r b ) + ( b x o r c ) + ( c x o r a ) = n a,b,c\ s.t.\ (a\ \mathrm{xor}\ b)+(b\ \mathrm{xor}\ c)+(c\ \mathrm{xor}\ a)=n a,b,c s.t. (a xor b)+(b xor c)+(c xor a)=n.
思路
注意到 a x o r a = 0 , a x o r 0 = a a\ \mathrm{xor}\ a=0,a\ \mathrm{xor}\ 0=a a xor a=0,a xor 0=a,不妨固定 b = 0 b=0 b=0,则 a + c + ( c x o r a ) + c = n a+c+(c\ \mathrm{xor}\ a)+c=n a+c+(c xor a)+c=n.
不妨再固定 c = 0 c=0 c=0,则 a + a = n a+a=n a+a=n,当 n n n为偶数时有解, a = n 2 a=\dfrac{n}{2} a=2n.
代码
int main() {
CaseT{
int n; cin >> n;
if (n & 1) {
cout << "-1" << endl;
continue;
}
cout << n / 2 << " 0 0" << endl;
}
}
Codeforces Round #804 (Div. 2) B. Almost Ternary Matrix
原题指路:https://codeforces.com/contest/1699/problem/B
题意
有 t ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t (1≤t≤100)组测试数据.每组测试数据给定 n , m ( 2 ≤ n , m ≤ 50 ) n,m\ \ (2\leq n,m\leq 50) n,m (2≤n,m≤50),求一个 n × m n\times m n×m的 0 − 1 0-1 0−1矩阵$\ s.t.\ 对 每 个 元 素 对每个元素 对每个元素(i,j) , 其 上 下 左 右 四 个 相 邻 的 元 素 ( 若 存 在 ) 恰 有 两 个 与 ,其上下左右四个相邻的元素(若存在)恰有两个与 ,其上下左右四个相邻的元素(若存在)恰有两个与(i,j)$不同.
思路
构造题,考察是否有可以构造出大的解的小的单元.
观察样例 2 × 2 2\times 2 2×2的情况:
易想到能否将其复制一份放在右边作为 2 × 4 2\times 4 2×4的解,但事实上不行.
观察样例 2 × 4 2\times 4 2×4的情况:
易想到可将 2 × 2 2\times 2 2×2的单元镜像一下再放到右边.
猜想能否将 2 × 4 2\times 4 2×4的单元镜像一下再放到下面作为 4 × 4 4\times 4 4×4的解,显然可行.
考虑用 4 × 4 4\times 4 4×4的单元构造更大的解.显然将其复制一份放到右边即可.
可先打出一个 4 × 4 4\times 4 4×4的解 a n s ans ans,输出答案矩阵的元素 ( i , j ) (i,j) (i,j)时,只需输出 a n s [ i % 4 ] [ j % 4 ] ans[i\%4][j\%4] ans[i%4][j%4].
代码
string ans[4] = { "1001",
"0110",
"0110",
"1001" };
int main() {
CaseT{
int n,m; cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++)
cout << ans[i % 4][j % 4] << (j == m - 1 ? endl : ' ');
}
}
}
Codeforces Round #804 (Div. 2)
原题指路:https://codeforces.com/contest/1699/problem/C
题意
对两个 0 ∼ ( n − 1 ) 0\sim (n-1) 0∼(n−1)的排列 a : { a 1 , ⋯ , a n } a:\{a_1,\cdots,a_n\} a:{a1,⋯,an}和 b : { b 1 , ⋯ , b n } b:\{b_1,\cdots,b_n\} b:{b1,⋯,bn},若对 ∀ [ l , r ] ( 1 ≤ l ≤ r ≤ n ) \forall [l,r]\ \ (1\leq l\leq r\leq n) ∀[l,r] (1≤l≤r≤n),有 m e x ( [ a l , ⋯ , a r ] ) = m e x ( [ b l , ⋯ , b r ] ) \mathrm{mex}([a_l,\cdots,a_r])=\mathrm{mex}([b_l,\cdots,b_r]) mex([al,⋯,ar])=mex([bl,⋯,br]),则称 a a a与 b b b相似.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据给定一个 0 ∼ ( n − 1 ) 0\sim (n-1) 0∼(n−1)的排列 a : { a 1 , ⋯ , a n } a:\{a_1,\cdots,a_n\} a:{a1,⋯,an},求与 a a a相似的 0 ∼ ( n − 1 ) 0\sim (n-1) 0∼(n−1)的排列的个数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.
思路
观察样例知,数 0 0 0的位置不变.枚举 0 ∼ ( n − 1 ) 0\sim (n-1) 0∼(n−1),每次维护区间 [ l , r ] [l,r] [l,r]的端点和当前可选位置的个数 c n t cnt cnt,考察下一个数 i i i在排列 a a a中的下标 p o s pos pos,有两种情况:
①若 l ≤ p o s ≤ r l\leq pos\leq r l≤pos≤r,则 i i i可放在区间 [ l , r ] [l,r] [l,r]中的任一位置而不改变 m e x ( [ l , r ] ) \mathrm{mex}([l,r]) mex([l,r]),此时 a n s ∗ = c n t ans*=cnt ans∗=cnt,放了一个数, c n t − − cnt-- cnt−−.
如排列 x x 1 x 3 x 0 x x x 2 x\ \ x\ 1\ x\ 3\ x\ 0\ x\ x\ x\ 2 x x 1 x 3 x 0 x x x 2中,若维护以数 1 1 1、数 0 0 0分别为左、右端点的区间,则数 3 3 3在该区间内可任意放,而不改变该区间的 m e x = 0 \mathrm{mex}=0 mex=0.
②若 p o s < l pos<l pos<l,可选位置增加 [ p o s , l ] [pos,l] [pos,l]内的数的个数(不含 p o s pos pos和 l l l),即 c n t + = l − p o s − 1 cnt+=l-pos-1 cnt+=l−pos−1,并更新当前维护的区间为 [ p o s , r ] [pos,r] [pos,r].
③若 p o s > r pos>r pos>r,可选位置增加 [ r , p o s ] [r,pos] [r,pos]内的数的个数(不含 r r r和 p o s pos pos),即 c n t + = p o s − r − 1 cnt+=pos-r-1 cnt+=pos−r−1,并更新当前维护的区间为 [ l , p o s ] [l,pos] [l,pos].
输入排列 a a a时记录 0 ∼ ( n − 1 ) 0\sim (n-1) 0∼(n−1)中每个数 a [ i ] a[i] a[i]在 a a a中的下标 i d x [ a [ i ] ] idx[a[i]] idx[a[i]].
代码I
template<class T>
struct MOD_NUM { // 数自动对MOD取模
T num;
static int MOD;
T norm(T x) { // 假设-MOD≤x<2*MOD
if (x < 0) x += MOD;
if (x >= MOD) x -= MOD;
return x;
}
MOD_NUM(T _num = 0) :num(norm(_num)) {}
T val()const { return num; }
MOD_NUM inv()const { assert(num); return qpow(*this, MOD - 2, MOD); } // MOD为素数
MOD_NUM& operator-()const { return MOD_NUM(norm(MOD - num)); }
MOD_NUM& operator*=(const MOD_NUM& rhs) { num = (ll)num * rhs.num % MOD; return *this; }
MOD_NUM& operator+=(const MOD_NUM& rhs) { num = norm(num + rhs.num); return *this; }
MOD_NUM& operator-=(const MOD_NUM& rhs) { num = norm(num - rhs.num); return *this; }
MOD_NUM& operator/=(const MOD_NUM& rhs) { return *this *= rhs.inv(); }
friend MOD_NUM operator*(const MOD_NUM& lhs, const MOD_NUM& rhs) { MOD_NUM res = lhs; res *= rhs; return res; }
friend MOD_NUM operator+(const MOD_NUM& lhs, const MOD_NUM& rhs) { MOD_NUM res = lhs; res += rhs; return res; }
friend MOD_NUM operator-(const MOD_NUM& lhs, const MOD_NUM& rhs) { MOD_NUM res = lhs; res -= rhs; return res; }
friend MOD_NUM operator/(const MOD_NUM& lhs, const MOD_NUM& rhs) { MOD_NUM res = lhs; res /= rhs; return res; }
friend bool operator==(const MOD_NUM& lhs, const MOD_NUM& rhs) { return lhs.val() == rhs.val(); }
friend istream& operator>>(istream& stream, MOD_NUM& p) { stream >> p.num; return stream; }
friend ostream& operator<<(ostream& stream, const MOD_NUM& p) { stream << p.val(); return stream; }
};
template<class T>
int MOD_NUM<T>::MOD = 1e9 + 7;
int main() {
CaseT{
int n; cin >> n;
vi a(n), idx(n); // 数、下标
for (int i = 0; i < n; i++) {
cin >> a[i];
idx[a[i]] = i;
}
int l = idx[0], r = idx[0]; // 维护的区间初始为元素0的下标
int cnt = 0; // 当前可选的位置的个数
MOD_NUM<int> ans = 1;
for (int i = 1; i < n; i++) { // 枚举数1到数(n-1)放的位置
int pos = idx[i]; // 数i的位置
if (l <= pos && pos <= r) { // 当前位置在[l,r]内部(含边界)
ans *= cnt; // 任意放,不改变区间mex
cnt--; // 该位置放了数i
}
else {
if (pos < l) {
cnt += l - pos - 1; // 更新可选位置个数
l = pos; // 更新所维护的区间
}
else { // pos>r
cnt += pos - r - 1; // 更新可选位置个数
r = pos; // 更新所维护的区间
}
}
}
cout << ans << endl;
}
}
代码II:省去维护 c n t cnt cnt
int main() {
CaseT{
int n; cin >> n;
vi a(n), idx(n); // 数、下标
for (int i = 0; i < n; i++) {
cin >> a[i];
idx[a[i]] = i;
}
int l = idx[0], r = idx[0]; // 维护的区间初始为元素0的下标
MOD_NUM<int> ans = 1;
for (int i = 1; i < n; i++) { // 枚举数1到数(n-1)放的位置
int pos = idx[i]; // 数i的位置
if (pos < l) l = pos;
else if (pos > r) r = pos;
else ans *= (r - l + 1 - i); // l≤pos≤r
}
cout << ans << endl;
}
}