CF1864题解

CF1864

CF1864A

link

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+1ai 严格单调递减。

如果无解输出 − 1 -1 1,多测。

CF1864A题解

a n ← y a_n\gets y any,然后 a i = a i + 1 − ( n − i + 1 ) a_i=a_{i+1}-(n-i+1) ai=ai+1(ni+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

link

CF1864C题意

给定一个数字 x ( x ≤ 1 0 9 ) x(x\le10^9) x(x109),你想把他变成 1 1 1
你可以对一个数字 a a a 进行一个操作:

  • 找到 a a a 的一个因数 b b b
  • a ← a − b a\gets a - b aab
  • 特别的,每一个 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

link

CF1864D题意

给出一个 n × n ( n ≤ 3 × 1 0 3 ) n\times n(n\le 3\times10^3) n×n(n3×103) 01 01 01 矩阵。
你可以进行一种操作,选择一个点 ( i , j ) (i,j) (i,j),让所有满足 x ≥ i , x − i ≥ ∣ y − j ∣ x\ge i,x-i\ge|y-j| xi,xiyj 的点 ( 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

link

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 1n 中分别等概率选取两个数 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 1T105 1 ≤ n , ∑ n ≤ 2 × 1 0 5 1 \leq n,\sum n \leq 2 \times 10^{5} 1n,n2×105 0 ≤ s i < 2 30 0 \leq s_{i} < 2^{30} 0si<230

CF1864E题解

典中典之Eric没有脑子。

我们想想这俩BYD在干什么。
Alice 编号是 0 0 0Bob 编号是 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 optopt ˆ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 optopt ˆ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

link

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,q106,1ain

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,p11],[p1+1,p21],,[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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值