2019牛客多校第三场

2019牛客多校第三场

A. Graph Games

upsolved

\(n\)个点,\(m\)条边的图\((1<=n<=1e5,1<=m,q<=2e5)\)\(q\)次操作,操作有两种,一种是翻转区间内边的状态,第二种是询问两个点的邻接点集是否一致

直接判断点集肯定\(T\)飞了,给每个点随机一个权值,点集的权值就是全部异或起来,冲突概率很小

对线段分块,复杂度可以达到\(O(q\sqrt m)\)(代码里快读删了)

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e5 + 10, M = 2e5 + 10, sz = 510;
 
long long val[N], s[N], sum[sz][N];
int l[sz], r[sz], belong[M];
int u[M], v[M], lazy[sz];
int T, n, m, q, op, blk, num, x, y;
 
void build() {
    blk = sqrt(m);
    num = 1 + (m - 1) / blk;
    for(int i = 1; i <= num; ++i) {
        l[i] = (i - 1) * blk + 1;
        r[i] = i * blk;
        lazy[i] = 0;
        for(int j = 1; j <= n; ++j)
            sum[i][j] = 0;
    }
    r[num] = m;
    for(int i = 1; i <= m; ++i)
        belong[i] = (i - 1) / blk + 1;
    for(int i = 1; i <= n; ++i)
        s[i] = 0;
}
 
void update(int l, int r) {
    if(belong[l] == belong[r]) {
        for(int i = l; i <= r; ++i) {
            s[u[i]] ^= val[v[i]];
            s[v[i]] ^= val[u[i]];
        }
        return;
    }
    for(int i = l; belong[i] == belong[l]; ++i) {
        s[u[i]] ^= val[v[i]];
        s[v[i]] ^= val[u[i]];
    }
    for(int i = r; belong[i] == belong[r]; --i) {
        s[u[i]] ^= val[v[i]];
        s[v[i]] ^= val[u[i]];
    }
    for(int i = belong[l] + 1; i < belong[r]; ++i)
        lazy[i] ^= 1;
}
 
int main() {
    srand(time(NULL));
    for(int i = 1; i <= 100000; ++i) val[i] = 1 + rand();
    read(T);
    while(T--) {
        read(n); read(m);
        build();
        for(int i = 1; i <= m; ++i) {
            read(u[i]); read(v[i]);
            s[u[i]] ^= val[v[i]];
            s[v[i]] ^= val[u[i]];
            sum[belong[i]][u[i]] ^= val[v[i]];
            sum[belong[i]][v[i]] ^= val[u[i]];
        }
        read(q);
        while(q--) {
            read(op); read(x); read(y);
            if(op == 1)
                update(x, y);
            else {
                long long a = s[x], b = s[y];
                for(int i = 1; i <= num; ++i) if(lazy[i]) {
                    a ^= sum[i][x];
                    b ^= sum[i][y];
                }
                printf("%d", (int)(a == b));
            }
        }
        puts("");
    }
    return 0;
}

B. Crazy Binary String

solved at 00:16

签到

D. Big Integer

upsolved 队友做的

F. Planting Trees

upsolved

\(O(n^3)\)单调队列,我是傻逼

#include <bits/stdc++.h>
using namespace std;

const int N = 505;

int d[N][N], n, m, T, ans, mx[N], mn[N];
int minn[N], maxx[N];
int h_mn, t_mn, h_mx, t_mx;

int main() {
    scanf("%d", &T);
    while(T--) {
        ans = 0;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) 
                scanf("%d", &d[i][j]);
        }
        for(int up = 1; up <= n; ++up) {
            memset(mx, 0, sizeof(mx));
            memset(mn, 0x3f, sizeof(mn));
            for(int down = up; down <= n; ++down) {
                h_mn = t_mn = h_mx = t_mx = 0;
                for(int k = 1, left = 0; k <= n; ++k) {
                    mx[k] = max(mx[k], d[down][k]);
                    mn[k] = min(mn[k], d[down][k]);
                    for(; h_mn != t_mn && mn[minn[t_mn - 1]] > mn[k]; ) t_mn--;
                    minn[t_mn++] = k;
                    for(; h_mx != t_mx && mx[maxx[t_mx - 1]] < mx[k]; ) t_mx--;
                    maxx[t_mx++] = k;
                    for(; h_mn != t_mn && h_mx != t_mx && mx[maxx[h_mx]] - mn[minn[h_mn]] > m;) {
                        if(maxx[h_mx] < minn[h_mn])
                            left = maxx[h_mx++];
                        else
                            left = minn[h_mn++];
                    }
                    ans = max(ans, (down - up + 1) * (k - left));
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

G. Removing stones

upsolved

等价于询问有多少个区间的区间最大值小于等于区间和

题解说是找最大值然后枚举左右两边较短的那一边的端点然后二分查另一边的端点再分治,看了别人写了个暴力枚举不合法区间的然后跑的贼快,复杂度不太会算

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 10;

int a[N], T, n;
long long ans;

int main() {
    scanf("%d", &T);
    while(T--) {
        ans = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) 
            scanf("%d", &a[i]);
        for(int i = 1; i <= n; ++i) {
            int l = i, r = i;
            long long sum = 0;
            while(l > 1 && sum + a[l - 1] < a[i]) l--, sum += a[l];
            while(r < n && sum + a[r + 1] < a[i]) r++, sum += a[r];
            ans += r - i + 1;
            for(int j = l; j < i; ++j) {
                sum -= a[j];
                while(r < n && sum + a[r + 1] < a[i]) r++, sum += a[r];
                ans += r - i + 1;
            } 
        }
        printf("%lld\n", 1LL * n * (n + 1) / 2- ans);
    }
    return 0;
}

H. Magic Line

solved at 00:36

要求用一条直线把平面上的点分成两部分,两部分个数相同且不能有点在线上

注意到点的坐标范围只有\(1000\)而你最终输出的线的坐标可以到\(1e9\)

一定可以用一条近似竖直线的线

把点分开

I. Median

upsolved

给定原序列的中位数序列(长度为\(n-2\)),构造一个原序列

J. LRU management

solved at 04:59(+22)

队友做的,他手写了一个链表。。。

用几个map维护就好了,利用map的迭代器可以自增或是自减的性质就好了

那个string可以看成一个11进制数(区分前置零)

我在贴上来的时候把快读删掉了,就是read那个函数的实现,用的是fread

#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;
int T, q, m, op, v;
long long ss;
char s[12];
map<int, long long> ptos;
map<long long, int> stop, stov;

long long convert(char s[]) {
    long long val = 0;
    for(int i = 0; s[i]; ++i) {
        val = val * 11 + s[i] - '0' + 1;
    }
    return val;
}

int main() {
    read(T);
    while(T--) {
        ptos.clear(); stop.clear(); stov.clear();
        read(q); read(m);
        ptos[100000] = 1e18;
        stop[1000000000000000000LL] = 100000;
        stov[1000000000000000000LL] = 100;
        while(q--) {
            read(op); read(s); read(v);
            ss = convert(s);
            if(op == 0) {
                if(stov.count(ss)) {
                    printf("%d\n", stov[ss]);
                    int fi = ptos.begin()->first;
                    int pos = stop[ss];
                    ptos.erase(ptos.find(pos));
                    stop[ss] = fi - 1;
                    ptos[fi - 1] = ss;
                }
                else {
                    stov[ss] = v;
                    int fi = ptos.begin()->first;
                    stop[ss] = fi - 1;
                    ptos[fi - 1] = ss;
                    if(ptos.size() > m) {
                        long long es = ptos.rbegin()->second;
                        ptos.erase(--ptos.end());
                        stop.erase(stop.find(es));
                        stov.erase(stov.find(es));
                    }
                    printf("%d\n", v);
                }
            }
            else {
                if(!stov.count(ss)) {
                    puts("Invalid");
                    continue;
                }
                int pos = stop[ss];
                if(v == 0) {
                    printf("%d\n", stov[ss]);
                }
                else {
                    map<int, long long>::iterator it = ptos.find(pos);
                    if(v == 1) {
                        if(it == ptos.begin()) {
                            puts("Invalid");
                            continue;
                        }
                        it--;
                        if(stov[it->second] == 100) {
                            puts("Invalid");
                            continue;
                        }
                        printf("%d\n", stov[it->second]);
                    }
                    else {
                        if(++it == ptos.end()) {
                            puts("Invalid");
                            continue;
                        }
                        if(stov[it->second] == 100) {
                            puts("Invalid");
                            continue;
                        }
                        printf("%d\n", stov[it->second]);
                    }
                }
            }
        }
    }
    return 0;
}

用map维护string(实际上是数)到list<int>::iterator的映射似乎写起来更简单

转载于:https://www.cnblogs.com/tusikalanse/p/11247289.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值