ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JOISC 2022 乱做

2022-09-08 13:31:41  阅读:153  来源: 互联网

标签:dep fa int pb JOISC 2022 id define


非传统题不做。

Day1 T1 Jail

给定一棵 \(n\) 个点的树,有 \(m\) 个人,第 \(i\) 个人从 \(s_i\) 出发要到 \(t_i\),每次可以指定一个人走一条边。问是否存在一种方案让每个人都到 \(t_i\),且满足任何两个人不同时出现在同一个节点。

\(m \leq n \leq 1.2 \times 10^5\),\(s_i\) 互不相同,\(t_i\) 互不相同,\(s_i \neq t_i\)。

solution

我们断言,一个人的移动一定是连续的,这点通过调整容易说明。考虑什么时候无解,感性理解一下发现就是有若干个人的路径形成了一个环的结构,这启发我们试着对这 \(n\) 个人移动的顺序进行约束。具体来说,对于两个人 \(i,j\),若 \(s_i \in (s_j,t_j)\),那么我们连边 \(i \to j\) 表示 \(i\) 要在 \(j\) 之前移动;若 \(t_i \in (s_j,t_j)\),那么我们连边 \(j \to i\) 表示 \(j\) 要在 \(i\) 之前移动。然后跑拓扑排序即可,无解当且仅当连出来的图成环。连边用倍增优化,时间复杂度 \(O((n+m) \log n)\)。

code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v) memset(x, v, sizeof(x))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
using namespace std;

inline int read() {
	int x = 0, w = 1;char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * w;
}

const int MN = 2e5 + 5;
const int MM = 5e6 + 5;
const int Mod = 1e9 + 7;

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }

inline int qPow(int a, int b = Mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = ret * a % Mod;
        a = a * a % Mod, b >>= 1;
    }
    return ret;
}

#define dbg

int N, M, s[MN], t[MN];
int dep[MN], deg[MM], fa[MN][20], up[MN][20], down[MN][20], id;
int dfn[MN], dfc, sz[MN];
vector <int> e[MN], w[MM];
queue <int> q;

inline void DFS(int u, int pr) {
    dep[u] = dep[fa[u][0] = pr] + 1;
    dfn[u] = ++dfc, sz[u] = 1;
    up[u][0] = ++id;
    down[u][0] = ++id;
    for (int i = 1; i <= 17; i++) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
        up[u][i] = ++id;
        w[up[u][i - 1]].pb(id); 
        if (fa[u][i - 1]) w[up[fa[u][i - 1]][i - 1]].pb(id);
        down[u][i] = ++id;
        w[id].pb(down[u][i - 1]);
        if (fa[u][i - 1]) w[id].pb(down[fa[u][i - 1]][i - 1]);
    }
    for (int v : e[u]) 
        if (v != pr) DFS(v, u), sz[u] += sz[v];
}
inline void add1(int id, int x, int y) {
    if (dfn[x] <= dfn[y] && dfn[y] < dfn[x] + sz[x]) {
        for (int i = 17; i >= 0; i--)
            if (dep[fa[y][i]] >= dep[x]) w[up[y][i]].pb(id), y = fa[y][i];
    } else {
        x = fa[x][0];
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 17; i >= 0; i--) 
            if (dep[fa[x][i]] >= dep[y]) w[up[x][i]].pb(id), x = fa[x][i];
        if (x == y) w[up[x][0]].pb(id);
        else {
            for (int i = 17; i >= 0; i--)  
                if (fa[x][i] != fa[y][i]) 
                    w[up[x][i]].pb(id), w[up[y][i]].pb(id), x = fa[x][i], y = fa[y][i];

            w[up[x][0]].pb(id), w[up[y][1]].pb(id);
        }
    }
}
inline void add2(int id, int x, int y) {
    if (dfn[y] <= dfn[x] && dfn[x] < dfn[y] + sz[y]) {
        for (int i = 17; i >= 0; i--)
            if (dep[fa[x][i]] >= dep[y]) w[id].pb(down[x][i]), x = fa[x][i];
    } else {
        y = fa[y][0];
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 17; i >= 0; i--) 
            if (dep[fa[x][i]] >= dep[y]) w[id].pb(down[x][i]), x = fa[x][i];
        if (x == y) w[id].pb(down[x][0]);
        else {
            for (int i = 17; i >= 0; i--)  
                if (fa[x][i] != fa[y][i]) 
                    w[id].pb(down[x][i]), w[id].pb(down[y][i]), x = fa[x][i], y = fa[y][i];

            w[id].pb(down[x][0]), w[id].pb(down[y][1]);
        }
    }
}
inline void work() {
    N = read(), dfc = 0;
    for (int i = 1; i <= N; i++) e[i].clear();
    for (int i = 1; i <= id; i++) w[i].clear(), deg[i] = 0;
    for (int i = 1, x, y; i < N; i++) {
        x = read(), y = read();
        e[x].pb(y), e[y].pb(x);
    }
    M = read(), id = M;
    DFS(1, 0);
    for (int i = 1, x, y; i <= M; i++) {
        x = read(), y = read();
        w[i].pb(up[x][0]), w[down[y][0]].pb(i);
        add1(i, x, y), add2(i, x, y);
    }
    int fl = 0;
    for (int i = 1; i <= id; i++) for (int x : w[i]) deg[x]++;
    for (int i = 1; i <= id; i++) if (!deg[i]) q.push(i);
    while (!q.empty()) {
        int x = q.front(); q.pop(), fl += x <= M;
        for (int y : w[x]) 
            if (!(--deg[y])) q.push(y);
    }
    puts(fl == M ? "Yes" : "No");
}

signed main(void) {
    int T = read();
    while (T--) work();
    return 0;
}

Day1 T2 Sightseeing in Kyoto

给定 \(n \times m\) 的网格图,第 \(i\) 行所有边权值为 \(a_i\),第 \(j\) 列所有边权值为 \(b_j\),每次只能向右或者向下移动,求 \((1,1)→(n,m)\) 的最短路。

\(n,m \leq 10^5\)。

solution

先考虑简单的情况,如果只经过一次转弯要从 \((i,j) \to (x,y)\),那么有两种可能的路径:\((i,j) \to (i,y) \to (x,y)\) 和 \((i,j) \to (x,j) \to (x,y)\)。这两条路径所对应的大小分别是 \((y-j)a_i + (x-i) b_y\) 和 \((x-i) b_j + (y-j) a_x\)。稍作化简可以得到当 \(\frac{a_x - a_i}{x - i} > \frac{b_y - b_j}{y - j}\) 时我们会选择第一条路,否则会选择第二条。

对于当前的所有行和列,把行和列分别看成点 \((i,a_i)\) 和 \((j,b_j)\),上式相当于比较了两个斜率。不妨先关注所有 \(2 \times 2\) 的小方格,处理出所有相邻位置(包括行和列)的斜率,那么对于斜率最大的两个点而言(假设是第 \(i\) 行和第 \(i+1\) 行),容易发现我们一定不会经过第 \(i+1\) 行,因此可以直接将第 \(i+1\) 行删去。

于是我们可以直接模拟上述过程,用 set 维护当前所有相邻点的斜率,每次取出最大的一个,删掉对应行 / 列并更新相邻点的斜率,如果删掉该行 / 列后 \((1,1)\) 和 \((n,m)\) 不连通就更新答案,维护一下从 \((n,m)\) 开始的已经确定的路径延伸到哪里就行了。时间复杂度 \(O((n+m) \log (n+m))\)。

还可以继续优化:回顾我们求解答案的过程,每次取斜率最大的位置删除,然后如果该位置是最靠右的点就计入答案;不难发现对于行和列而言,只有在下凸包上的点可能对答案产生贡献。于是我们可以直接对行和列求下凸包,这样相邻位置的斜率都是单调的,那么每次只会删除结尾位置的点,用双指针维护即可。当然也可以从前往后用双指针维护决策,反正是等价的。时间复杂度 \(O(n+m)\)。

code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v) memset(x, v, sizeof(x))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
using namespace std;

inline int read() {
	int x = 0, w = 1;char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * w;
}

const int MN = 1e5 + 5;
const int Mod = 1e9 + 7;

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }

inline int qPow(int a, int b = Mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = ret * a % Mod;
        a = a * a % Mod, b >>= 1;
    }
    return ret;
}

#define dbg

int N, M, a[MN], b[MN], p[MN], q[MN], l, r;

signed main(void) {
    N = read(), M = read();
    for (int i = 1; i <= N; i++) a[i] = read();
    for (int i = 1; i <= M; i++) b[i] = read();
    p[l = 1] = q[r = 1] = 1;
    for (int i = 2; i <= N; i++) {
        while (l > 1 && (a[i] - a[p[l]]) * (p[l] - p[l - 1]) <= (a[p[l]] - a[p[l - 1]]) * (i - p[l])) l--;
        p[++l] = i;
    }
    for (int i = 2; i <= M; i++) {
        while (r > 1 && (b[i] - b[q[r]]) * (q[r] - q[r - 1]) <= (b[q[r]] - b[q[r - 1]]) * (i - q[r])) r--;
        q[++r] = i;
    }
    int x = 1, y = 1, s = 1, t = 1, ans = 0;
    while (x < N || y < M) {
        if (s == l || (t != r && (a[p[s + 1]] - a[p[s]]) * (q[t + 1] - q[t]) > (b[q[t + 1]] - b[q[t]]) * (p[s + 1] - p[s]))) 
            ans += (q[t + 1] - q[t]) * a[x], y = q[++t];
        else
            ans += (p[s + 1] - p[s]) * b[y], x = p[++s];
    }
    printf("%lld\n", ans);
    return 0;
}

Day1 T3 Misspelling

对于由小写字母组成的字符串 \(S\),定义字符串 \(T_i\) 为 \(S\) 删去第 \(i\) 个字符并将前后字符串相接所得的字符串。给定 \(m\) 个条件,每个条件形如 \((s_i,t_i)\) 表示 \(T_{s_i}\) 的字典序不大于 \(T_{b_i}\)。求有多少个长度为 \(n\) 的字符串 \(S\) 满足所有条件。答案对 \(10^9 + 7\) 取模。

\(n,m \leq 5 \times 10^5\)。

标签:dep,fa,int,pb,JOISC,2022,id,define
来源: https://www.cnblogs.com/came11ia/p/16667178.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有