CSP-S模拟13 [排序,Xorum,有趣的区间问题,无聊的卡牌问题](好久没更过考试总结了呢)

CSP-S模拟13

今天的破防除了考试保龄以外,还有自己的涩图题解传到了hsez的同学那里,属实是完完全全的社死了。

image
image

丢大人了啊。TST将会臭名昭著

A.排序

CF1375E Inversion SwapSort

给定长度为 \(n\) 的序列 \(a_i\),求一种将每个逆序对下标 \((u,v)\) 的排序,使依次交换每个 \((a_u,a_v)\) 后,\(a_i\) 不减。

其实考试题是弱化版,原题是一个序列,考试题是一个排列。序列会出现重复的数,会更难处理一些,我们先假定 \(a\) 是一个排列。

因为最后要 \(a_i\) 不减,所以最后每个数放在哪里是固定的。要求交换每个逆序对,我们可以 \(n^2\) 扫一遍求出所有的逆序对,记录下每组逆序对在 \(a\) 的位置。

之后去考虑每组逆序对交换的顺序。由于要交换逆序对的个数次,所以在每次交换后逆序对数只能减 \(1\),即每一次交换只能影响当前交换的逆序对,不能影响其他的逆序对。比如:\(5,4,2,3,1\),\(5\) 不能直接和 \(1\) 交换,这样 \(4,2,3\) 和 \(1\) 构成的逆序对都会被影响,交换次数就假掉了。

所以,我们要优先交换 \(a\) 值大的逆序对。详细一点,对于逆序对 \((a_i,a_j)\),在 \(a_i\) 构成的所有逆序对中,我们要先交换 \(a_j\) 小的。对于 \(a_i\) 不同,优先要交换 \(i\) 小的。

对于序列其实同理,只是会有相等的情况。同样,对于逆序对 \((a_i,a_j)\),如果 \(a_j\) 相同,先交换 \(j\) 小的。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

#define Pair pair< int, int >
#define Make(x, y) make_pair(x, y)

using namespace std;

const int MAXN = 1010;
int n, cnt;
int num[MAXN], val[MAXN];
vector< Pair > out;

bool cmp(const Pair &x, const Pair &y){
    if(x.first == y.first){
        if(num[x.second] != num[y.second]) return num[x.second] > num[y.second];
        return x.second > y.second;
    }
    return x.first < y.first;
}

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read();
    for(register int i = 1; i <= n; i++){
        num[i] = read();
        val[i] = num[i];
    }

    sort(val + 1, val + 1 + n);
    cnt = unique(val + 1, val + 1 + n) - val - 1;
    for(register int i = 1; i <= n; i++){
        int pos = lower_bound(val + 1, val + 1 + cnt, num[i]) - val;
        num[i] = pos;
    }    
    
    for(register int i = 1; i <= n; i++)
        for(register int j = i + 1; j <= n; j++)
            if(num[i] > num[j]) out.push_back(Make(i, j));
    
    sort(out.begin(), out.end(), cmp);

    printf("%ld\n", out.size());
    for(register int i = 0; i < out.size(); i++)
        printf("%d %d\n", out[i].first, out[i].second);
    
    return 0;
}

B.Xorsum

CF1427E Xum

给出的 \(x\) 一定为奇数,则它二进制表示下的最低位一定是 \(1\),所以我们考虑如何把 \(x\) 其他位置的 \(1\) 消去。

依次消除 \(x\) 当前的最高位。注意对同一个数进行相加操作相当于左移一位,想要让 \(x\) 的最低位和最高位对齐,一共需要左移 \((x)_{2}\) 的位数 \(-1\) 位,设这个数为 \(y\)。当 \(x\) 异或 \(y\) 后,\(x\) 的最高位就被消掉了,设这个数为 \(z\)。

记 \(r = y + z\),\(r\) 的低位和 \(z\) 相同,高位相当于 \(y\) 的高位左移一位,中间不同的一位是因为 \(y\) 和 \(z\) 相加最高位的进位产生。之后我们将 \(y\) 左移一位,记作 \(s\)。当我们将 \(s\) 与 \(r\) 异或后,得到 \(t\),\(t\) 的最高位和 \(y\) 的最高位相同,其余位和 \(x\) 相同,\(t\) 异或 \(x\) 就可以得到 \(t\) 的 \(highbit\),记为 \(u\)。我们用 \(u\) 每次消去 \(y\) 的一个 \(1\),直到 \(y\) 只剩下最低位的 \(1\),这样就得到了 \(x\) 的 \(highbit\),就可以消掉 \(x\) 的最高位。一直消下去,直到 \(x\) 为 \(1\) 即可。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

#define LL long long

using namespace std;

LL x;

struct Answer{
    LL opt, x, y;
};

vector<Answer> out;

LL lowbit(LL x){
    return x & (-x);
}

LL Change(LL x){
    LL y = x, res = x >> 1/*每次需要左移 x 的二进制表示的位数 - 1位*/;
    while(res){
        out.push_back((Answer){1, y, y});
        y <<= 1;
        res >>= 1;
    }
    //这时 y 的最低次位已经和 x 的最高次位对齐了
    LL z = x ^ y; //消去最高次位上的 1 
    out.push_back((Answer){2, x, y});
    //printf("x = %lld y = %lld z = %lld\n", x, y, z);
    //然后去找 x 的 highbit,也就是把 y 消到只剩下一个 1
    LL r = y + z;
    out.push_back((Answer){1, y, z});
    LL s = y + y;
    out.push_back((Answer){1, y, y});
    LL t = r ^ s;
    out.push_back((Answer){2, r, s});
    LL u = t ^ x; 
    out.push_back((Answer){2, t, x});

    while(y != lowbit(y)){
        if(y & u){
            out.push_back((Answer){2, y, u});
            y ^= u;
        } 
        out.push_back((Answer){1, u, u});       
        u += u;
        
        //printf("y = %lld\n", y);
    }

    //printf("first x = %lld\n", x);

    out.push_back((Answer){2, x, y});
    x ^= y;
    

    return x;
}

int main(){
    scanf("%lld", &x);

    while(x != 1) x = Change(x);

    printf("%ld\n", out.size());
    for(register int i = 0; i < out.size(); i++){
        if(out[i].opt == 1) printf("%lld + %lld\n", out[i].x, out[i].y);
        else if(out[i].opt == 2) printf("%lld ^ %lld\n", out[i].x, out[i].y);
    }
    
    return 0;
}

C.有趣的区间问题

CF1609F Interesting Sections

考虑枚举最大值和最小值的 popcount,那么问题等价于,给定一个序列 \(a_i\),其中有一些位置是关键位置,要求有多少个区间,满足其最大值和最小值都是关键位置。

枚举区间问题无非两个套路,扫描线和CDQ,这里用扫描线。枚举右端点 \(r\),扫描到 \(r\) 时,我们记 \(f_l\) 表示 \([l,r]\) 中最大值是否关键位置,以及 \([l,r]\) 中最小值是否是关键位置,那么显然 \(f_l\) 的变化可以通过单调栈求出,那么对于一个右端点 \(r\) 和一个 popcount \(v\),其对答案的贡献就是 \(f_l=2\) 的 \(l\) 的个数。注意到 \(f_l\) 的上界就是 \(2\),因此这个可以通过区间最大值及其出现次数的线段树维护。

以及,这题卡常,用一下 fread 之类的差不多就能卡过去了。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXN = 1e6 + 10, SIZE = 65;
int n;
LL ans;
LL num[MAXN];
int cnt[MAXN], temp[SIZE];
int stk_max[MAXN], top_max, stk_min[MAXN], top_min;

struct Change{
    int pos, l, r, v;
};

vector<Change> q[SIZE];

inline char gc(){
	static char buf[MAXN], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXN, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL read(){
	LL x = 0, f = 1;
    char c = gc();
	
    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = gc();
    }
	while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = gc();
    }

	return x * f;
}

inline LL lowbit(LL x){
    return x & (-x);
}

inline int popcount(LL x){
    int ans = 0;
    while(x){
        ++ans;
        x -= lowbit(x);
    }
    return ans;
}

struct Segment_Tree{
    struct Tree{
        int l, r;
        int sum;
        int max;
        int lazy;
    }tr[MAXN << 2];

    inline int lson(int rt){
        return rt << 1;
    }

    inline int rson(int rt){
        return rt << 1 | 1;
    }

    inline void Pushup(int rt){
        tr[rt].sum = 0;
        tr[rt].max = max(tr[lson(rt)].max, tr[rson(rt)].max);
        if(tr[rt].max == tr[lson(rt)].max) tr[rt].sum += tr[lson(rt)].sum;
        if(tr[rt].max == tr[rson(rt)].max) tr[rt].sum += tr[rson(rt)].sum;
    }

    void Build(int rt, int l, int r){
        tr[rt].l = l;
        tr[rt].r = r;
        tr[rt].max = 0;
        tr[rt].lazy = 0;
        tr[rt].sum = r - l + 1;

        if(l == r) return;

        int mid = (l + r) >> 1;
        Build(lson(rt), l, mid);
        Build(rson(rt), mid + 1, r);
    }

    inline void Pushdown(int rt){
        if(tr[rt].lazy){
            tr[lson(rt)].max += tr[rt].lazy;
            tr[rson(rt)].max += tr[rt].lazy;
            tr[lson(rt)].lazy += tr[rt].lazy;
            tr[rson(rt)].lazy += tr[rt].lazy;
            tr[rt].lazy = 0;
        }
    }

    void Update(int rt, int l, int r, int data){
        if(tr[rt].l >= l && tr[rt].r <= r){
            tr[rt].max += data;
            tr[rt].lazy += data;
            return;
        }

        Pushdown(rt);

        int mid = (tr[rt].l + tr[rt].r) >> 1;
        if(l <= mid) Update(lson(rt), l, r, data);
        if(r > mid) Update(rson(rt), l, r, data);

        Pushup(rt);
    }
}S;

int main(){
    n = read();
    for(register int i = 1; i <= n; ++i){
        num[i] = read();
        cnt[i] = popcount(num[i]);
        ++temp[cnt[i]];
    }

    //for(register int i = 1; i <= n; i++)
    //    printf("cnt[%d] = %d\n", i, cnt[i]);

    for(register int i = 1; i <= n; ++i){
        while(top_min && num[stk_min[top_min]] > num[i]){
            int p = stk_min[top_min--];
            q[cnt[p]].push_back((Change){i, stk_min[top_min] + 1, p, -1});
        }
        q[cnt[i]].push_back((Change){i, stk_min[top_min] + 1, i, 1});
        stk_min[++top_min] = i;

        while(top_max && num[stk_max[top_max]] < num[i]){
            int p = stk_max[top_max--];
            q[cnt[p]].push_back((Change){i, stk_max[top_max] + 1, p, -1});
        }
        q[cnt[i]].push_back((Change){i, stk_max[top_max] + 1, i, 1});
        stk_max[++top_max] = i;
    }

    for(register int i = 0; i <= 64; ++i){
        if(!temp[i]) continue;

        S.Build(1, 1, n);

        for(register int j = 1, sit = 0; j <= n; ++j){
            while(sit < q[i].size() && q[i][sit].pos <= j){
                S.Update(1, q[i][sit].l, q[i][sit].r, q[i][sit].v);
                ++sit;
            }

            if(S.tr[1].max == 2) ans += S.tr[1].sum;
        }
    }
    
    printf("%lld\n", ans);

    return 0;   
}

D.无聊的卡牌问题

CF1427F Boring Card Game

没搞懂呢,咕。

搞懂了,就是细节有点恶心,搞了一个上午。

Code

#include<queue>
#include<cstdio>
#include<algorithm>

using namespace std;

const int MAXN = 1210;
int n, tot/*联通块的数量*/, num/*不存在依赖关系的为 0 的联通快的数量*/;
int card[MAXN], belong[MAXN]/*这张牌归谁取*/, type[MAXN]/*联通快的类型*/, size[MAXN]/*联通快的大小*/;
int deg[MAXN]/*联通块的入度*/, son[MAXN]/*联通块的儿子*/;
int block[MAXN][4]; //每个联通块的成员
int stk[MAXN], top;
bool vis[MAXN];
queue<int> q1, q2;

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read() * 3;
    for(register int i = 1; i <= n; i++){
        card[i] = read();
        belong[card[i]] = 1; //是先手
    }

    n <<= 1;
    for(register int i = 1; i <= n; i++){
        if(!top || type[stk[top]] != belong[i]){
            stk[++top] = ++tot;
            type[tot] = belong[i];
            ++size[tot];
            block[tot][size[tot]] = i;
        }
        else{
            int p = stk[top];
            ++size[p];
            block[p][size[p]] = i;

            if(size[p] == 3){ //弹栈
                int q = stk[top - 1];
                son[p] = q;
                ++deg[q];
                --top;
            }
        }
    }

    for(register int i = 1; i <= tot; i++) //去找不存在依赖关系的为 0 的联通快
        if(!deg[i] && !son[i] && !type[i]) ++num;

    for(register int i = 1; i <= tot; i++){
        if(!deg[i] && type[i]) q1.push(i);
        if(!deg[i] && !type[i]) q2.push(i);
    }

    for(register int tim = 1; tim <= tot; tim++){ //第几次取
        if(tim & 1){ //到先手取
            while(!q1.empty()){
                int t = q1.front();
                q1.pop();

                vis[t] = true;
                if(son[t]){
                    --deg[son[t]];
                    if(!deg[son[t]] && !vis[son[t]]){
                        q2.push(son[t]);
                        if(!son[son[t]]) ++num;
                    }
                }
                printf("%d %d %d\n", block[t][1], block[t][2], block[t][3]);
                break;
            }
        }
        else{
            while(!q2.empty()){
                int t = q2.front();
                q2.pop();

                if(tim < tot && !son[t] && num == 1 && !q2.empty()){
                    q2.push(t);
                    continue;
                }

                vis[t] = true;
                if(son[t]){
                    --deg[son[t]];
                    if(!deg[son[t]] && !vis[son[t]]) q1.push(son[t]);
                }
                if(!son[t]) --num;
                printf("%d %d %d\n", block[t][1], block[t][2], block[t][3]);
                break;
            }
        }
    }

    return 0;
}
posted @ 2022-09-28 08:56  TSTYFST  阅读(135)  评论(7)    收藏  举报