CF1864题解
CF1864
CF1864A
CF1864A题意
给出三个数字 x , y , n x,y,n x,y,n,要你构造一个长度是 n n n 的序列 a a a,满足:
- a a a 严格单调递增。
- a a a 的差分数组 b i = a i + 1 − a i b_i=a_{i+1}-a_i bi=ai+1−ai 严格单调递减。
如果无解输出 − 1 -1 −1,多测。
CF1864A题解
让 a n ← y a_n\gets y an←y,然后 a i = a i + 1 − ( n − i + 1 ) a_i=a_{i+1}-(n-i+1) ai=ai+1−(n−i+1),然后就没了。
CF1864A代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e3 + 10;
int x, y, n;
int a[maxn];
signed main(){
int T = read();
while(T--){
x = read();y = read();n = read();
a[n] = y;bool flag = true;
for(int i = 1;i < n;i++){
a[n - i] = a[n - i + 1] - i;
// printf("i = %d, dec = %d\n",i,dec);
if(a[n - i] < x){flag = false;break;}
}
if(!flag){puts("-1");}
else{
a[1] = x;
for(int i = 1;i <= n;i++)printf("%d ",a[i]);
puts("");
}
}
return 0;
}
CF1864B
略
CF1864C
CF1864C题意
给定一个数字
x
(
x
≤
1
0
9
)
x(x\le10^9)
x(x≤109),你想把他变成
1
1
1。
你可以对一个数字
a
a
a 进行一个操作:
- 找到 a a a 的一个因数 b b b。
- 令 a ← a − b a\gets a - b a←a−b。
- 特别的,每一个 b b b 只能取不多于 2 2 2 次,并且你的操作次数不能超过 1 0 3 10^3 103 次。
给出方案。
CF1864C题解
Eric再次被爆杀。
考虑到对于一个数
x
x
x,
l
o
w
b
i
t
(
x
)
lowbit(x)
lowbit(x) 一定是
x
x
x 的一个因数。
于是每次都减去
l
o
w
b
i
t
(
x
)
lowbit(x)
lowbit(x) 直到
x
x
x 在二进制表示中只有一个位置是
1
1
1。
上面的操作对每个
b
b
b 只用了一次,然后考虑怎样将这个
1
1
1 挪到最后一位。
思路很显然,直接每次都减去
1
1
1 的下一位,这样这个
1
1
1 就挪到了最后一位。
这里对每个
b
b
b 同样只用了一次。
二进制保证了你的操作次数不超过
1
0
3
10^3
103 次(甚至没超过
100
100
100 次)。
没了。
CF1864C代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
inline int lowbit(int x){return x & (-x);}
vector<int> opt;
signed main(){
int t = read();
while(t--){
int n = read();opt.clear();
while(n > 1){
int x = lowbit(n);if(n - x < 1)break;
opt.push_back(n); n = n - x;
}
if(n != 1){
opt.push_back(n);
for(int i = 30;i + 1;i--){
if(n > (1 << i)){
n -= (1 << i);
opt.push_back(n);
}
}
}
printf("%d\n",opt.size());
for(int i : opt)printf("%d ",i);
puts("");
}
return 0;
}
CF1864D
CF1864D题意
给出一个
n
×
n
(
n
≤
3
×
1
0
3
)
n\times n(n\le 3\times10^3)
n×n(n≤3×103) 的
01
01
01 矩阵。
你可以进行一种操作,选择一个点
(
i
,
j
)
(i,j)
(i,j),让所有满足
x
≥
i
,
x
−
i
≥
∣
y
−
j
∣
x\ge i,x-i\ge|y-j|
x≥i,x−i≥∣y−j∣ 的点
(
x
,
y
)
(x,y)
(x,y) 中的数字取反。
求最少操作数,多测。
CF1864D题解
诈骗题。
首先发现这个所谓的最少操作数是假的,只要你不傻就能想到每个位置最多操作一次,于是就变成给出一种操作方案求总和。
为什么?接着往下看。
不难发现,每一个操作
(
i
,
j
)
(i,j)
(i,j) 对
∀
x
<
j
,
∀
y
∈
[
1
,
n
]
,
(
x
,
y
)
\forall x<j,\forall y\in[1,n],(x,y)
∀x<j,∀y∈[1,n],(x,y) 和
∀
y
≠
j
,
(
i
,
y
)
\forall y\neq j,(i,y)
∀y=j,(i,y) 都没有影响。
于是从上到下操作,你就会发现这个最少操作是假的。
如果暴力操作是
O
(
n
4
)
O(n^4)
O(n4) 的,这不T飞了,于是尝试打标记。
手玩一下发现不能简单的打标记,需要额外打向左拓展和向右拓展的标记。
打标记有点麻烦,慢慢写吧。
CF1846D代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e3 + 10;
int n;
char ch[maxn][maxn];
bool a[maxn][maxn], b[maxn][maxn];
bool lf[maxn][maxn], ri[maxn][maxn], down[maxn][maxn];
bool opt[maxn][maxn];
signed main(){
int T = read();
while(T--){
n = read();
for(int i = 1;i <= n;i++){
scanf("%s",ch[i] + 1);
for(int j = 1;j <= n;j++){
a[i][j] = ch[i][j] - '0';
down[i][j] = ri[i][j] = lf[i][j] = opt[i][j] = 0;
}
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
if(down[i - 1][j]){down[i][j] ^= 1;}
if(lf[i - 1][j]){down[i][j - 1] ^= 1;lf[i][j - 1] ^= 1;}
if(ri[i - 1][j]){down[i][j + 1] ^= 1;ri[i][j + 1] ^= 1;}
}
for(int j = 1;j <= n;j++){
a[i][j] ^= down[i][j];
// printf("%d ",a[i][j]);
if(a[i][j]){
opt[i][j] = 1;a[i][j] ^= 1;
lf[i][j] ^= 1;down[i][j] ^= 1;ri[i][j] ^= 1;
}
}
// for(int j = 1;j <= n;j++)printf("%d ",down[i][j] + ((int)lf[i][j] << 1) + ((int)ri[i][j] << 2));
// puts("");
}
int ans = 0;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
ans += opt[i][j];
printf("%d\n",ans);
// for(int i = 1;i <= n;i++){
// for(int j = 1;j <= n;j++)
// printf("%d ",opt[i][j]);
// puts("");
// }
}
return 0;
}
CF1864E
CF1864E题意
Carol 要和 Alice 和 Bob 玩猜数游戏。
首先 Carol 有一个长为 n n n 的数列 s = { s i } s = \{s_{i}\} s={si}( i ∈ [ 1 , n ] i \in [1,n] i∈[1,n]),Carol 从 1 ∼ n 1 \sim n 1∼n 中分别等概率选取两个数 i a , i b i_{a},i_{b} ia,ib,注意 i a i_a ia 与 i b i_b ib 可以相等。
令 a = s i a a=s_{i_a} a=sia, b = s i b b=s_{i_b} b=sib。
a a a 和 a or b a \operatorname{or} b aorb 会被 Carol 告诉 Alice。
b b b 和 a or b a \operatorname{or} b aorb 会被 Carol 告诉 Bob。
但两人都不知道 s s s 数列的任何信息。
两人要猜出 a a a 与 b b b 的相对大小,即 a > b a > b a>b, a < b a < b a<b 或 a = b a = b a=b。
从Alice 开始轮流,每次轮到的人可以说“我不知道相对大小”然后轮给另一个人,或者说“我知道了”然后给出结果。
Alice 和 Bob 都是绝顶聪明并且只在有确凿把握时猜测,现在你要输出两人进行的期望轮数(每说一句话即一轮),对 998 244 353 998\,244\,353 998244353 取模。
本题有多组测试数据。
数据范围, 1 ≤ T ≤ 1 0 5 1 \leq T \leq 10^5 1≤T≤105, 1 ≤ n , ∑ n ≤ 2 × 1 0 5 1 \leq n,\sum n \leq 2 \times 10^{5} 1≤n,∑n≤2×105, 0 ≤ s i < 2 30 0 \leq s_{i} < 2^{30} 0≤si<230。
CF1864E题解
典中典之Eric没有脑子。
我们想想这俩BYD在干什么。
设 Alice 编号是
0
0
0,Bob 编号是
1
1
1,
d
[
0
]
=
a
,
d
[
1
]
=
b
,
d
[
2
]
=
a
or
b
d[0]=a,d[1]=b,d[2]=a\operatorname{or} b
d[0]=a,d[1]=b,d[2]=aorb,函数
u
p
b
i
t
(
x
,
i
)
upbit(x,i)
upbit(x,i) 表示在二进制表示中,从最高位开始
x
x
x 的第
i
i
i 个
1
1
1 的位置。
设当前操作的人的编号是
o
p
t
opt
opt,操作轮数是
k
k
k。
从第一轮操作开始考虑。
不难发现,如果
u
p
b
i
t
(
d
[
o
p
t
]
,
1
)
<
u
p
b
i
t
(
d
[
2
]
,
1
)
upbit(d[opt],1) < upbit(d[2],1)
upbit(d[opt],1)<upbit(d[2],1),那么一定有
d
[
o
p
t
]
<
d
[
o
p
t
ˆ
1
]
d[opt]<d[opt \^\ 1]
d[opt]<d[opt ˆ1]。
否则
u
p
b
i
t
(
d
[
o
p
t
]
,
1
)
=
u
p
b
i
t
(
d
[
2
]
,
1
)
upbit(d[opt],1) = upbit(d[2],1)
upbit(d[opt],1)=upbit(d[2],1),
o
p
t
←
o
p
t
ˆ
1
opt \gets opt\^\ 1
opt←opt ˆ1。
然后考虑第二轮操作。
因为上一个人传下来一定有
u
p
b
i
t
(
d
[
o
p
t
ˆ
1
]
,
1
)
=
u
p
b
i
t
(
d
[
2
]
,
1
)
upbit(d[opt\^\ 1],1) = upbit(d[2],1)
upbit(d[opt ˆ1],1)=upbit(d[2],1),那么这个人只需要判断下
u
p
b
i
t
(
d
[
o
p
t
]
,
1
)
upbit(d[opt],1)
upbit(d[opt],1) 与
u
p
b
i
t
(
d
[
2
]
,
1
)
upbit(d[2],1)
upbit(d[2],1) 的关系。
- 如果 u p b i t ( d [ o p t ] , 1 ) < u p b i t ( d [ 2 ] , 1 ) upbit(d[opt],1) < upbit(d[2],1) upbit(d[opt],1)<upbit(d[2],1),那么一定有 d [ o p t ] < d [ o p t ˆ 1 ] d[opt]<d[opt\^\ 1] d[opt]<d[opt ˆ1]
- 否则判断
u
p
b
i
t
(
d
[
o
p
t
]
,
2
)
upbit(d[opt],2)
upbit(d[opt],2) 与
u
p
b
i
t
(
d
[
2
]
,
2
)
upbit(d[2],2)
upbit(d[2],2) 的关系
- 如果 u p b i t ( d [ o p t ] , 2 ) < u p b i t ( d [ 2 ] , 2 ) upbit(d[opt],2) < upbit(d[2],2) upbit(d[opt],2)<upbit(d[2],2),那么一定有 d [ o p t ] < d [ o p t ˆ 1 ] d[opt]<d[opt\^\ 1] d[opt]<d[opt ˆ1]
- 否则 u p b i t ( d [ o p t ] , 2 ) = u p b i t ( d [ 2 ] , 2 ) upbit(d[opt],2) = upbit(d[2],2) upbit(d[opt],2)=upbit(d[2],2), o p t ← o p t ˆ 1 opt\gets opt\^\ 1 opt←opt ˆ1
不难发现第
k
k
k 轮操作都在判断
u
p
b
i
t
(
d
[
o
p
t
]
,
k
)
upbit(d[opt],k)
upbit(d[opt],k) 与
u
p
b
i
t
(
d
[
2
]
,
k
)
upbit(d[2],k)
upbit(d[2],k) 的关系,如果有不同就一定能判断出来。
再进一步,对于一对数字
(
a
,
b
)
(a,b)
(a,b),操作轮数
a
n
s
ans
ans 取决于
max
k
∈
[
0
,
30
]
u
p
b
i
t
(
a
,
k
)
=
u
p
b
i
t
(
b
,
k
)
k
\max_{k\in[0,30]}^{upbit(a,k)=upbit(b,k)}k
maxk∈[0,30]upbit(a,k)=upbit(b,k)k(设这个数字是
c
n
t
cnt
cnt)。\
不难发现,结论分三种情况:
- a = b a=b a=b: a n s = c n t + 1 ans=cnt+1 ans=cnt+1
- a < b a<b a<b: a n s = c n t + 1 + ( c n t % 2 ) ans=cnt+1+(cnt\%2) ans=cnt+1+(cnt%2)
- a > b a>b a>b: a n s = c n t + 1 + ( c n t % 2 ) ˆ 1 ans=cnt+1+(cnt\%2)\^\ 1 ans=cnt+1+(cnt%2) ˆ1
这个东西推了我半个小时+查看题解(
菜是原罪,菜就多练练
发现这玩应可以在
t
r
i
e
trie
trie 上面跑,并且考虑一次性将
i
=
a
,
j
=
b
i=a,j=b
i=a,j=b 和
i
=
b
,
j
=
a
i=b,j=a
i=b,j=a 一次性全求出来,这样就不用考虑两者大小关系了。
然后就没了。
总结,这玩应需要好好想想,想明白了之后码量也不大,是道不错的思维题。
upd:注意long long
CF1864E代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10;
const ll mod = 998244353;
int n;
ll ans;
struct Trie{
int tr[maxn * 31][2], cnt[maxn * 31], tot;
void clear(){for(int i = 1;i <= tot;i++)tr[i][0] = tr[i][1] = cnt[i] = 0;tot = 1;}
void insert(int x){
int u = 1;
for(int i = 30;i + 1;i--){
bool v = (x >> i) & 1LL;
if(!tr[u][v])tr[u][v] = ++tot;
u = tr[u][v];cnt[u]++;
}
}
}trie;
#define ls(u) trie.tr[(u)][0]
#define rs(u) trie.tr[(u)][1]
#define cnts(u) trie.cnt[(u)]
ll qpow(ll x,int a){
ll res = 1;
while(a){
if(a & 1)res = res * x % mod;
x = x * x % mod;a >>= 1;
}
return res;
}
void dfs(int u,int dep,int cnt){
if(!u)return;
if(dep == -1){
ans = (ans + (ll)cnts(u) * cnts(u) % mod * (cnt + 1) % mod) % mod;
return;
}
ans = (ans + (ll) cnts(ls(u)) * cnts(rs(u)) % mod * (cnt * 2 + 3) % mod) % mod;
dfs(ls(u),dep - 1,cnt );
dfs(rs(u),dep - 1,cnt + 1);
}
signed main(){
int T = read();
while(T--){
trie.clear(); n = read();
for(int i = 1;i <= n;i++){trie.insert(read());}
ans = 0;dfs(1,30,0);
printf("%lld\n",ans * qpow(qpow(n,2),mod - 2) % mod);
}
return 0;
}
CF1864F
CF1864F题意
给定一个长为
n
n
n 的序列,有
q
q
q 个询问,每次询问
l
,
r
l,r
l,r ,要求对于每次独立的询问,最小化操作次数
m
m
m,使得所有
a
i
∈
[
l
,
r
]
a_i \in [l,r]
ai∈[l,r] 的
a
i
a_i
ai 都变成零。对于一次操作,可以任意选取一个连续段,将其统一减去一个非负整数,并且这些段之间不能相交,但可以包含。
n
,
q
≤
1
0
6
,
1
≤
a
i
≤
n
n,q\le10^6,1\le a_i\le n
n,q≤106,1≤ai≤n
CF1864F题解
先想想如果查询
[
1
,
n
]
[1,n]
[1,n] 怎么做。
不难发现,第一次一定减去的是区间
[
1
,
n
]
[1,n]
[1,n] 的最小值。
然后所有最小值位置(设为
p
1
,
2
,
.
.
.
,
k
p_{1,2,...,k}
p1,2,...,k)都不能再动了,这样的话,这些位置把整个区间分成了
[
1
,
p
1
−
1
]
,
[
p
1
+
1
,
p
2
−
1
]
,
…
,
[
p
k
+
1
,
n
]
[1,p_1 - 1],[p_1 + 1,p_2-1],\dots,[p_k+1,n]
[1,p1−1],[p1+1,p2−1],…,[pk+1,n] 等若干个小区间,对于每个小区间又要进行相同的操作…
但是这样做的话是
O
(
n
)
O(n)
O(n) 的,带上查询就T飞了。
不妨换个角度想想,对于最近的
a
i
=
a
j
a_i=a_j
ai=aj,考虑这两个能不能一起处理。
不难发现,如果将所有小于
a
i
a_i
ai 的数字插入,并查询
[
i
,
j
]
[i,j]
[i,j] 之间的最大值(设为
x
x
x),那么对于所有包含
[
x
,
a
i
]
[x,a_i]
[x,ai] 的查询,这对
i
,
j
i,j
i,j 的贡献都是
1
1
1。
这玩应显然可以扫描线。
然后就没了。
CF1864F代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e6 + 10;
int n, q;
vector<int> vec[maxn];
struct Segment_Tree{
struct node{
int maxx, summ,tag;
int l, r;
node(int maxx = 0,int summ = 0,int l = 0,int r = 0,int tag = 0):maxx(maxx),summ(summ),tag(tag),l(l),r(r){}
}d[maxn << 2];
node mergenode(node l,node r){return node(max(l.maxx,r.maxx),l.summ + r.summ,l.l,r.r);}
void gettag(int p,int add){
d[p].tag += add;
d[p].summ += add * (d[p].r - d[p].l + 1);
d[p].maxx += add;
}
void pushdown(int p){
if(d[p].tag){
gettag(p << 1,d[p].tag);gettag(p << 1 | 1,d[p].tag);
d[p].tag = 0;
}
}
void build(int l,int r,int p){
if(l == r){d[p] = node(0,0,l,l);return;}
int mid = l + r >> 1;
build(l,mid,p << 1);build(mid + 1,r,p << 1 | 1);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
void update(int l,int r,int s,int t,int p,int add){
if(s <= l && r <= t){gettag(p,add);return;}
int mid = l + r >> 1;pushdown(p);
if(s <= mid)update(l,mid,s,t,p << 1,add);
if(mid < t)update(mid + 1,r,s,t,p << 1 | 1,add);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
node query(int l,int r,int s,int t,int p){
if(s <= l && r <= t)return d[p];
int mid = l + r >> 1;pushdown(p);
if(t <= mid)return query(l,mid,s,t,p << 1);
if(mid < s)return query(mid + 1,r,s,t,p << 1 | 1);
return mergenode(query(l,mid,s,t,p << 1),query(mid + 1,r,s,t,p << 1 | 1));
}
void update(int s,int t,int add){update(0,n,s,t,1,add);}
node query(int s,int t){return query(0,n,s,t,1);}
}tree1,tree2;
vector<pair<int,int> > qry[maxn];
int ans[maxn], sum[maxn];
signed main(){
n = read();q = read();int x, y;
for(int i = 1;i <= n;i++){vec[read()].push_back(i);}
for(int i = 1;i <= n;i++){sum[i] = sum[i - 1] + !vec[i].empty();}
for(int i = 1;i <= q;i++){x = read(); y = read();qry[y].push_back(make_pair(x,i));}
tree1.build(0,n,1);tree2.build(0,n,1);
for(int i = 1;i <= n;i++){
for(int j = 1;j < vec[i].size();j++){
x = vec[i][j - 1];y = vec[i][j];
tree2.update(0,tree1.query(x,y).maxx,1);
}
for(int j : vec[i])tree1.update(j,j,i);
for(auto j : qry[i])
ans[j.second] = sum[i] - sum[j.first - 1] + tree2.query(j.first,j.first).summ;
}
for(int i = 1;i <= q;i++)printf("%d\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号