2018-2019, ICPC, Asia Yokohama Regional Contest 2018 (Gym - 102082)

Posted ruthank

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018-2019, ICPC, Asia Yokohama Regional Contest 2018 (Gym - 102082)相关的知识,希望对你有一定的参考价值。

2018-2019, ICPC, Asia Yokohama Regional Contest 2018

A - Digits Are Not Just Characters

签到。


B - Arithmetic Progressions

题意:从给定的集合中选出最多的数构成等差数列。

题解:数字排序后,设\(dp[i][j]\)表示等差数列最后一个数字为\(a[i]\),倒数第二个数字为\(a[j]\)的最大个数。然后对于每一位枚举 \(i\)\(lower\_bound()\)找有无合法的 \(j\) 即可。复杂度\(O(n^2log(n))\)

代码:

#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
using namespace std;
const int maxn = 5000 + 5;

int dp[maxn][maxn];
int a[maxn];
int n;

int main() 
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    sort(a+1, a+1+n);

    int ans = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j < i; j++) 
            int idx = lower_bound(a+1, a+1+n, a[j]-(a[i]-a[j])) - a;
            if (a[j] - a[idx] == a[i] - a[j])
                dp[i][j] = max(dp[i][j], dp[j][idx]+1);
            ans = max(ans, dp[i][j]);
        
    printf("%d\n", ans+2);

C - Emergency Evacuation

题意:一些人在车座里,求所有人下车的最小时间。

题解:考虑到门口距离相等的两个人会在某个地方相遇,所以其中一个人要多花费一个单位的时间。故可以求出每个人到门口的距离,排序后处理相等的,直到序列变为升序。


D - Shortest Common Non-Subsequence

题意:给出两个\(01\)\(s\)\(t\),求最短的 \(01\) 序列,不是 \(s\)\(t\) 的子序列。多组答案输出字典序最小的。

题解:序列自动机。预处理出\(s\)\(t\)每个位置上后面第一个\(1\)和第一个\(0\)的位置。设当前在\(s\)\(t\)的位置分别为\((x,y)\),则可以由\((next_s[x][1], next_t[y][1])\)\((next_s[x][0],next_t[y][0])\)转移到,分别代表当前位放\(1\)\(0\)。则答案就是从\((0,0)\)\((len_s+1, len_t+1)\)的字典序最小的最短路。

代码:

#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int maxn = 4000 + 5;
const int inf = 0x3f3f3f3f;

int dp[maxn][maxn], fa[maxn][maxn];
char s[maxn], t[maxn];
int nexta[maxn][2], nextb[maxn][2];
int l1, l2;
vector<int> ans;

int dfs(int x, int y) 
    if (x == l1+1 && y == l2+1) return 0;
    if (dp[x][y]) return dp[x][y];
    int tmp1 = dfs(nexta[x][0], nextb[y][0]),
        tmp2 = dfs(nexta[x][1], nextb[y][1]);
    if (tmp1 <= tmp2) fa[x][y] = -1; else fa[x][y] = 1;
    return dp[x][y] = min(tmp1, tmp2)+1;


void Print(int x, int y) 
    if (x == l1+1 && y == l2+1) return;
    int res = fa[x][y] > 0;
    ans.push_back(res);
    Print(nexta[x][res], nextb[y][res]);


int main() 
    scanf("%s%s", s+1, t+1);
    l1 = strlen(s+1), l2 = strlen(t+1);

    nexta[l1+1][1] = nexta[l1+1][0] = l1+1;
    nextb[l2+1][1] = nextb[l2+1][0] = l2+1;
    for (int i = l1; i >= 0; i--) 
        nexta[i][1] = nexta[i+1][1];
        nexta[i][0] = nexta[i+1][0];
        if (s[i+1] == '1') nexta[i][1] = i+1;
        else nexta[i][0] = i+1;
    

    for (int i = l2; i >= 0; i--) 
        nextb[i][1] = nextb[i+1][1];
        nextb[i][0] = nextb[i+1][0];
        if (t[i+1] == '1') nextb[i][1] = i+1;
        else nextb[i][0] = i+1;
    

    dfs(0, 0);
    Print(0, 0);

    for (auto p : ans) printf("%d", p);
    puts("");

E - Eulerian Flight Tour

题意:在一个图上任意添加一些边,使得图中存在欧拉回路。输出添加的边。

题解:

存在欧拉回路 \(=\) 所有点的度数为偶数。

把每条可用的边设为一个未知数(值为\(0\)\(1\))。把每个点关于可用的边按度数写出一个方程,使得所有点的度数都为偶数。解非齐次线性方程组计算,计算出用哪些边(未知数值为1)。

若此时图已联通,那么这就是答案。如果此时图的连通块个数\(>=3\),可以加入一些边使它们成环。如果连通块的个数为\(2\),且每个连通块内都存在两点没有直接相连,那么就把这四个点连成环。

若方程组无解或无法联通,则无解。


G - What Goes Up Must Come Down

题意:通过交换相邻两个数的形式,把一个序列变为非递减\(+\)非递增两部分。求操作次数。

题解:小的数肯定要去大的外层。所以考虑每一个数走到两边的逆序对,每次取逆序对小的一侧。求和即为答案。求逆序对可以用树状数组,来回求两次。

代码:

#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;

int n;
int a[maxn];
struct Trray 
    int C[maxn], N = 100000;
    void init()  memset(C, 0, sizeof(C)); 
    int lowbit(int x)  return x & (-x); 
    void update(int i, int k) 
        while(i <= N) C[i] += k, i += lowbit(i);
    
    int getsum(int i) 
        int res = 0;
        while(i > 0) res += C[i], i -= lowbit(i);
        return res;
    
Tr;

int pre[maxn];

int main() 
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) 
        scanf("%d", &a[i]);
        Tr.update(a[i], 1);
        pre[i] = i - Tr.getsum(a[i]);
    

    Tr.init();
    LL ans = 0;
    for (int i = n; i >= 1; i--) 
        Tr.update(a[i], 1);
        ans += min(pre[i], n-i+1 - Tr.getsum(a[i]));
    
    printf("%lld\n", ans);

K - Sixth Sense

题意:你和对手都有\(n\)个数字。你可以任意改变自己的数字顺序,对于每个位置,若自己的数字大,则得一分。求保证得分最多的前提下,字典序最大的顺序方案。

题解:先贪心求出最大得分。然后从前往后枚举每个位置,二分当前位置,在不改变得分的前提下,可用的最大数字。注意当前位置要分为得分和不得分两种情况,分别二分(如果不分情况直接二分,例如敌方\(1\ 2\ 3\)/我方\(1\ 2\ 3\),第一局用\(1\)是会改变最大得分的,此时会被判定为不可以,\(mid\)会变小)。

代码:

#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
using namespace std;
const int maxn = 5000 + 5;

int a[maxn], b[maxn], p[maxn];
int ans[maxn];
multiset<int> S, tep;
int n, x, num, now, tot;

bool check(int st, int mid) 
    int res = 0, id = 1;
    for (int i = st; i <= n; i++) 
        while(id == mid || (id <= tot && b[id] <= a[i])) id++;
        if (id <= tot) res++, id++;
    
    return res + (b[mid] > a[st-1]) + now == num;


int main() 
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) 
        scanf("%d", &a[i]);
        p[i] = a[i];
    

    for (int i = 1; i <= n; i++) 
        scanf("%d", &x);
        tep.insert(x);
        S.insert(x);
    

    for (int i = 1; i <= n; i++) 
        multiset<int> :: iterator it = tep.upper_bound(a[i]);
        if (it == tep.end()) continue;
        if (*it > a[i]) num++, tep.erase(it);
    

    for (int i = 1; i <= n; i++) 
        tot = 0;
        for (auto p : S) b[++tot] = p;

        copy(p+1, p+1+n, a+1);
        sort(a+i+1, a+1+n);

        int idx = upper_bound(b+1, b+1+tot, a[i])-b;
        int l = idx, r = tot, tmp = -1;
        while(b[idx] > a[i] && l <= r) 
            int mid = (l+r) / 2;
            if (check(i+1, mid)) tmp = mid, l = mid+1;
            else r = mid-1;
        

        if (tmp == -1) 
            int l = 1, r = idx-1;
            while(l <= r) 
                int mid = (l+r) / 2;
                if (check(i+1, mid)) tmp = mid, l = mid+1;
                else r = mid-1;
            
        

        multiset<int> :: iterator it = S.lower_bound(b[tmp]);
        S.erase(it);

        if (b[tmp] > a[i]) now++;
        ans[i] = b[tmp];
    

    for (int i = 1; i <= n; i++)
        printf("%d%c", ans[i], i == n ? '\n' : ' ');

以上是关于2018-2019, ICPC, Asia Yokohama Regional Contest 2018 (Gym - 102082)的主要内容,如果未能解决你的问题,请参考以下文章

2018-2019 ACM-ICPC, Asia Dhaka Regional Contest

训练20191009 2018-2019 ACM-ICPC, Asia East Continent Finals

2019.05.02 第九次训练 2018-2019 ACM-ICPC, Asia Jiaozuo Regional Contest

2018-2019 ACM-ICPC, Asia Seoul Regional Contest

2018-2019 ACM-ICPC, Asia Xuzhou Regional Contest Solution

2018-2019 ACM-ICPC, Asia Seoul Regional Contest