CF Round #625 Div1

我的第 100 场参与排名的 Codeforces 记录一下

contest id 1320

A. Journey Planning

给一个长度为 \(n\) 的序列 \(b\),求子序列中满足原下标差与序列两项差相等的最大和

就是说有个序列 \(b\) ,求出一个子序列 \(x\) ,它在原序列中的下标依次为 \(c\),则对每个 \(i\) 都有

\(c_{i+1}-c_i = b_{c_{i+1}}-b_{c_i}\)

\(\sum x\) 的最大值

显然对于 \(i-b_i\) 相同的值都是可以放在同一个子序列中的,贪心丢进去就行了,图方便没有去判断正负直接用 \(map\) 储存差值即可

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/3/1
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int n;
map<int, ll> mp;

int main(int argc, char* argv[]) {
    scanf("%d", &n);
    vector<int> a(n);
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
    for (int i = 0; i < n; ++i) {
        mp[a[i] - i] += a[i];
    }
    ll res = 0;
    for (auto i : mp) {
        if (i.second > res) res = i.second;
    }
    printf("%lld\n", res);
    return 0;
}

B. Navigation System

给一张有向图,边权全 1,给出行走路径,已知所有信息,求最少和最多需要改变多少次最短路径导航(也就是每次在一个点给出到终点的导航,但是实际路径没有按导航走,到下个点时换了另一条路)

Q:为什么会有多种情况?A:多条最短路的时候可能选择其中任何一条,需要分别求最小和最大值

数据范围:点、边、行走路径长度最多 \(2\cdot 10^5\)

首先考虑到换路问题需要当前点到终点的最短路,所以反向建图对终点求一次单源 dij

假设现在在 \(x\) 点,下一个点是 \(y\),如果 \(dist[x]=dist[y]+1\) 说明 \(x\to y\)\(x\to last\) 的最短路(可能是之一)

然后遍历 \(x\) 所有的后继节点(正向图)发现没有其他满足与 \(y\) 一样条件的点的话就是唯一最短路,那么两个答案都不变。如果有多条最短路只把最多次数 +1 即可。如果不是最短路就是两个答案都 +1

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/3/1
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5 + 50;
const int mod = 1e9 + 7;

ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template<class T>
inline bool scan(T &ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int n, m, k;
int dis[maxn];
vector<int> edge[maxn], inv[maxn];

void dijkstra(int s) {
    priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;
    memset(dis, 0x3f, sizeof dis);
    que.push({0, s});
    dis[s] = 0;
    while (!que.empty()) {
        auto f = que.top();
        que.pop();
        int u = f.second, d = f.first;
        if (d != dis[u]) continue;
        for (auto v : edge[u]) {
            if (dis[u] + 1 < dis[v]) {
                dis[v] = dis[u] + 1;
                que.push({dis[v], v});
            }
        }
    }
}

int main(int argc, char *argv[]) {
    scanf("%d%d", &n, &m);
    for (int i = 0, u, v; i < m; ++i) {
        scanf("%d%d", &u, &v);
        edge[v].push_back(u);
        inv[u].push_back(v);
    }
    scanf("%d", &k);
    vector<int> path(k);
    for (int i = 0; i < k; ++i) {
        scanf("%d", &path[i]);
    }
    dijkstra(path.back());
    int mnres = 0, mxres = 0;
    for (int i = 0; i < k - 1; ++i) {
        int mn = 0x3f3f3f3f, count = 0;
        for (auto v : inv[path[i]]) {
            if (dis[v] == mn) {
                count ++;
            } else if (dis[v] < mn) {
                mn = dis[v];
                count = 1;
            }
        }
        if (mn == dis[path[i + 1]] && count == 1) ;
        else if (mn == dis[path[i + 1]] && count > 1) mxres ++;
        else if (mn < dis[path[i + 1]]) mnres ++, mxres ++;
//        cerr << mn << " " << dis[path[i + 1]] << endl;
    }
    printf("%d %d\n", mnres, mxres);
    return 0;
}

C. World of Darkraft: Battle for Azathoth

\(n\) 个武器 \(m\)个防具(weapons and armor总感觉有点熟悉??某游戏mod制作者发出了鸽子的声音)

武器有攻击力,防具有防御力,每件装备都有它的花费

\(p\) 个怪物,每个都有其攻击力、防御力、价值

现在需要挑一件武器和一件防具,对于 \(p\) 个怪物,如果你选择的武器攻击力大于它的防御力、防具大于它的攻击力,则可以无损地获得它的价值。求最大价值 - 花费

\(1 \leq n, m, p \leq 2 \cdot 10^5\)

看到这种数据范围和两个能力值就知道是二维点对问题了

很套路的扫描线

假设一个二维空间 \(xoy\)\(x\) 轴表示攻击力轴,\(y\) 轴表示防御力轴(当然也可以反过来),每个怪物是离散的二维点,那么对于一组武器、防具的选择则有左下方的所有点都可以击败,也就是一个二维带权区间最值,总之对防具(\(y\) 轴)排序,对武器(\(x\) 轴)建立线段树求区间和,扫描线往上增加怪物的价值点求最大值即可。

具体来说,每个点的权值为:首先第一次将线段树上的所有点减去武器的花费,然后枚举每一个防具,最大值 - 它的花费就是答案。所有防具枚举完取最大值就是最后的答案。实际上每个怪物对大于它防御的武器都是有贡献的所以需要区间加、区间求最值。

我好啰嗦

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/3/1
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;

int n, m, p;
int val[maxn << 2], lazy[maxn << 2];
int a[maxn], ca[maxn];
int vala[maxn];

inline void build(int rt, int l, int r) {
    if (l == r) {
        val[rt] = -vala[l];
        return;
    }
    int mid = l + r >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
    val[rt] = max(val[rt << 1], val[rt << 1 | 1]);
}

inline void pushup(int rt) {
    val[rt] = max(val[rt << 1], val[rt << 1 | 1]);
}

inline void pushdown(int rt) {
    if (lazy[rt]) {
        lazy[rt << 1] += lazy[rt];
        lazy[rt << 1 | 1] += lazy[rt];
        val[rt << 1] += lazy[rt];
        val[rt << 1 | 1] += lazy[rt];
        lazy[rt] = 0;
    }
}

int le, k;
inline void update(int rt, int l, int r) {
    if (le <= l) {
        val[rt] += k;
        lazy[rt] += k;
        return;
    }
    int mid = l + r >> 1;
    pushdown(rt);
    if (le <= mid) update(rt << 1, l, mid);
    update(rt << 1 | 1, mid + 1, r);
    pushup(rt);
}

struct node {
    int x, y, z;
    bool operator < (const node& oth) const {
        return y < oth.y;
    }
};

vector<int> xpos;

int main(int argc, char* argv[]) {
    scanf("%d%d%d", &n, &m, &p);
    xpos.resize(n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d", &a[i], &ca[i]);
        xpos[i - 1] = a[i];
    }
    sort(xpos.begin(), xpos.end());
    xpos.erase(unique(xpos.begin(), xpos.end()), xpos.end());
    for (int i = 1; i <= n; ++i) vala[i] = 0x3f3f3f3f;
    for (int i = 1; i <= n; ++i) {
        int pos = lower_bound(xpos.begin(), xpos.end(), a[i]) - xpos.begin() + 1;
        vala[pos] = min(vala[pos], ca[i]);
    }
    build(1, 1, n);
    vector<pair<int, int> > b(m);
    for (int i = 0; i < m; ++i) {
        scanf("%d%d", &b[i].first, &b[i].second);
    }
    sort(b.begin(), b.end());
    vector<node> x(p);
    for (int i = 0; i < p; ++i) {
        scanf("%d%d%d", &x[i].x, &x[i].y, &x[i].z);
    }
    sort(x.begin(), x.end());
    int now = 0;
    ll res = -0x3f3f3f3f3f3f3f3f;
    for (int i = 0; i < m; ++i) {
        ll tmp = -b[i].second;
        while (now < p) {
            if (x[now].y < b[i].first) {
                le = upper_bound(xpos.begin(), xpos.end(), x[now].x) - xpos.begin() + 1;
                k = x[now].z;
                if (le <= n) update(1, 1, n);
                now ++;
            } else break;
        }
        tmp += val[1];
//        cerr << tmp << " " << i << endl;
        res = max(res, tmp);
    }
    printf("%lld\n", res);
    return 0;
}

D. Reachable Strings

给一个串,长度不超过 \(2 \cdot 10^5\)\(2 \cdot 10^5\) 次询问两个等长的子串 \(s1,s2\)

是否可达

所谓 \(s,t\) 可达

即是指对于串 \(s\) 任意次将子串 011 替换成 110 或者 110 替换成 011 能变为 \(t\)

首先,显然两个串的 0 的数量相等、1 的数量相等

其次,显然两个串的独立 1 相对位置不变,that's to say 010 can't be 100 or 001

那么就可以认定,在去掉所有相邻的一对 1 之后如果串相同则认为两个串可达

赛中其实是想到了结论的,然而写的实在是太丑了

线段树维护所有不相邻 1 的 hash 即可

很久没写过这么复杂的线段树了,合并属实难写,以至于我想一句句的说明它每句的意思

但还是算了,实在太麻烦了,总之维护区间左值(pflag)、右值(sflag)、已经删除了连续 1 的对数(del)、删除之后的长度(len)、删除之后的 hash 值(hs)即可

注意此处有可能需要删掉 hash 值的最后一个值和开头一个值,稍作分析就知道一定会产生除法,所以必须取模使用逆元。另外一提此题有其他简单写法,不过我没有看懂别人按奇偶分类的 hash 写法

/*================================================================
*
*   创 建 者: badcw
*   创建日期: 2020/3/4
*
================================================================*/
#include <bits/stdc++.h>

#define ll long long
using namespace std;

const int maxn = 2e5+50;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}

template <class T>
inline bool scan(T& ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; // EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

//template <class T>
//inline void out(T x) {
//    if (x > 9) out(x / 10);
//    putchar(x % 10 + '0');
//}

int n, q;
char s[maxn];
const int prim = 997;
const int invs = qp(prim, mod - 2);

ll base[maxn] = {1};

struct node {
	int len;
	int del;
    ll hs;
    bool pflag, sflag;
	bool operator == (const node& oth) const {
		return len == oth.len && del == oth.del && hs == oth.hs && pflag == oth.pflag && sflag == oth.sflag;
	}
}p[maxn << 2];

node comb(node a, node b) {	
	if (!a.len) {
		b.del += a.del;
		return b;
	}
	if (!b.len) {
		a.del += b.del;
		return a;
	}
	node ret;
	ret.del = a.del + b.del;
	if (a.sflag && b.pflag) {
		ret.del ++;
		a.hs = (a.hs - 1 + mod) * invs % mod;
		a.len --;
		a.sflag = 0;
		if (a.len == 0) a.pflag = 0;

		b.hs = (b.hs - base[b.len - 1] + mod) % mod;
		b.len --;
		b.pflag = 0;
		if (b.len == 0) b.sflag = 0;

		ret.len = a.len + b.len;
		ret.pflag = (a.len == 0 ? b.pflag : a.pflag);
		ret.sflag = (b.len == 0 ? a.sflag : b.sflag);

		ret.hs = (a.hs * base[b.len] % mod + b.hs) % mod;
	} else {
		ret.len = a.len + b.len;
		ret.hs = (a.hs * base[b.len] % mod + b.hs) % mod;
		ret.pflag = (a.len == 0 ? b.pflag : a.pflag);
		ret.sflag = (b.len == 0 ? a.sflag : b.sflag);
	}
	return ret;
}

void build(int rt, int l, int r) {
    if (l == r) {
        p[rt].len = 1;
        p[rt].del = 0;
        p[rt].hs = s[l] - '0';
        p[rt].sflag = p[rt].pflag = s[l] - '0';
        return;
    }
    int mid = l + r >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
    p[rt] = comb(p[rt << 1], p[rt << 1 | 1]);
}

int le, re;
node query(int rt, int l, int r) {
    if (le <= l && r <= re) {
        return p[rt];
    }
    int mid = l + r >> 1;
    node tmp{0, 0, 0, 0, 0};
    if (le <= mid) {
        tmp = comb(tmp, query(rt << 1, l, mid));
    }
    if (re > mid) {
        tmp = comb(tmp, query(rt << 1 | 1, mid + 1, r));
    }
    return tmp;
}

int main(int argc, char* argv[]) {
    for (int i = 1; i < maxn; ++i) base[i] = 1ll * base[i - 1] * prim % mod;
    scanf("%d%s%d", &n, s + 1, &q);
    build(1, 1, n);
    for (int i = 0; i < q; ++i) {
        int l1, l2, len;
        scanf("%d%d%d", &l1, &l2, &len);
        le = l1, re = l1 + len - 1;
        node p1 = query(1, 1, n);
        le = l2, re = l2 + len - 1;
        node p2 = query(1, 1, n);
        puts(p1 == p2 ? "YES" : "NO");
    }
    return 0;
}
posted @ 2020-03-06 03:36  badcw  阅读(195)  评论(0编辑  收藏  举报