The 2019 ICPC Asia-East Continent Final(部分题解)

这是今年我的最后一场,顺便写一下游记,第一场因为发挥打铁,心里很郁闷,加之最近学业加(我们学院都是硬件专业) 。那天考了四级(不能过)就和另外一个队一起坐高铁来到西安,到的时候时间还有还可以打一打热身赛,不得不说志愿者服务真好。
第一天热身赛,看了一眼,旁边都是比我们好的学校,直接签了A题,C++,JAVA都试了一下还不错,Clion好用,B题刚刚开始想对了,但不过被自己干掉了,然后试了一试机器,就走了,会宾馆点外卖。
第二天正式赛,其实挺慌的,不敢多开,就三个人看一道题,好在刚刚开场就有人过了A题,然后看A题,三个人反复确认没有读错后,大家一起写了一发,一发A,然后有人交了E,M题,就看E题和M题,M题刚刚我有点误解题意了,好在队友小G纠正过来了,然后小Z感觉E题题意不是很好,然后因为小G擅长图论,就让一起读懂了题意让他看了看,结果他想了10分钟,没有思路,然后小Z有想到了M题怎么做,我们让小G继续看看E,我和小Z写M,一会儿写出来了,测了很多数据没有问题,交WA,因为有了南京的前车之鉴,一下子看到了爆了int,该了一交A了,然后这时E题过得比H多,然后不知道怎么的大家好像多E题没有思路就都来看H题,H题一会儿也读懂了,然后大家都在想不知道怎么做,中间我口嗨了一个原根。。。然后忽然听到旁边有一个队说了一个n/2个数,肯定不会超过二的间隔不是2就是1,最多3,然后我们好像知道怎么做了,写了一会儿,WA一发,好在一下子就测出了,特殊样例,改了A了,然后还有两个小时,我们分别看了C题和E题,我想了30分钟C题确实想不到,然后我们去看E题,中间我想了个假算法,WA了,然后大家,想法层出不穷,没一个对的,然后就结束了,心想南京有这状态多好。好在最后混了一个银尾。。。。

A. City

本场比赛的唯一签到题
给一个网格,问有多少条线段两端是格点,同时中点也是格点,比赛时拿着这道题,想了一会儿,直接就上去写了,就是暴力统计,偶数长度边的数量,然后偶数偶数进行组合。

#include "bits/stdc++.h"


using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 100000 + 10;
const ll mod = 998244353;
int n, m;

int main() {
    cin >> n >> m;
    ll ans = 0;
    for (int i = 2; i <= n; i += 2)
        ans += 1ll * (m + 1) * (n - i + 1);
    for (int i = 2; i <= m; i += 2)
        ans += 1ll * (n + 1) * (m - i + 1);
    for (int i = 2; i <= n; i += 2) {
        for (int j = 2; j <= m; j += 2) {
            ans += 2ll * (n - i + 1) * (m - j + 1);
        }
    }
    cout << ans << endl;
    return 0;
}

C.Dirichlet k k k-th root

我想说的是,题意很好的,就是求一个函数f,他自己狄利克雷卷自己k次得到的g。
这是以前学习中不曾想过的,今天偶然发现一个函数卷自己mod+1次就是自己,mod次就是 [ n = 1 ] [n=1] [n=1]
把我惊倒了,我就想和mod有关,没想到开k次方根就是一个快速幂 i n v ( k ) inv(k) inv(k)次,交了一发居然过了。我也不知道,等能证明了再来补上。

#include "bits/stdc++.h"
 
 
using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 100000 + 10;
const ll mod = 998244353;
ll quick(ll a, ll n) {
    a %= mod;
    ll ans = 1;
    while (n) {
        if (n & 1) ans = ans * a % mod;
        n >>= 1;
        a = a * a % mod;
    }
    return ans;
}
int n, k;
ll f[maxn], g[maxn], s[maxn], ss[maxn], pro[maxn], inv_f[maxn];
void mult(ll a[], ll b[]) {
    memset(s, 0, sizeof(s));
    for (int i = 1; i * i <= n; i++) {
        s[i * i] = (s[i * i] + a[i] * b[i] % mod) % mod;
        for (int j = i + 1; i * j <= n; j++) {
            s[i * j] = (s[i * j] + a[i] * b[j] % mod + a[j] * b[i] % mod) % mod;
        }
    }
    for (int i = 1; i <= n; i++) a[i] = s[i];
}
 
void power(ll a[], int p) {
    for (int i = 1; i <= n; i++) ss[i] = a[i];
    memset(pro, 0, sizeof(pro));
    pro[1] = 1;
    while (p) {
        if (p & 1) mult(pro, ss);
        mult(ss, ss);
        p >>= 1;
    }
    for (int i = 1; i <= n; i++) a[i] = pro[i];
}
int main() {
    n = read(), k = read();
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &f[i]);
        g[i] = 1;
    }
    ll inv = quick(k, mod - 2);
    power(f, inv);
    printf("%lld", f[1]);
    for (int i = 2; i <= n; i++) {
        printf(" %lld", f[i]);
    }
    cout << endl;
    return 0;

E.Flow

本题题意,给你一个图,这个图很特殊,源点1到汇点n的长度都相等,而且每一条源点到汇点的路径都是独立路径互补相交,类似与链,你可让一条边加1另外一条边减去1,问使得流尽量大的最少操作数。比赛的假算法就算了吧。
能过的算法是这样的,对每一条链的流进行排序,并对每一条的流进行排序,然后统计每一条链排序后,把所有链一起,流扩到下一个结点的代价。然后就可以进行贪心了,枚举每一点,剩余流量能加就加上。

#include "bits/stdc++.h"


using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 200000 + 10;
struct edge {
    int v, next, w;
} ed[maxn];
int head[maxn], cnt = 0, n, m;
void add_edge(int u, int v, int w) {
    ++cnt;
    ed[cnt].v = v;
    ed[cnt].w = w;
    ed[cnt].next = head[u];
    head[u] = cnt;
}
vector<int> g[maxn];
void dfs(int u, int f) {
    for (int i = head[u]; i; i = ed[i].next) {
        int v = ed[i].v;
        if (v == f) continue;
        g[cnt].push_back(ed[i].w);
        dfs(v, u);
    }
}
ll addflow[maxn];
int main() {
    n = read(), m = read();
    int u, v, w, len;
    ll flow = 0;
    for (int i = 1; i <= m; i++) {
        u = read();
        v = read();
        w = read();
        add_edge(u, v, w);
//        add_edge(v, u, w);
        flow += w;
    }
    cnt = 0;
    for (int i = head[1]; i; i = ed[i].next) {
        v = ed[i].v;
        g[++cnt].push_back(ed[i].w);
        dfs(v, 1);
        len = g[cnt].size();
        sort(g[cnt].begin(), g[cnt].end());
        int l = 0;
        while (l < len - 1) {
            while (l < len - 1 && g[cnt][l] == g[cnt][l + 1]) l++;
            if (l < len) addflow[l + 1] += g[cnt][l + 1] - g[cnt][l];
            l++;
        }
        flow -= 1ll * g[cnt][0] * len;
    }
    ll ans = 0, tmp;
    for (int i = 1; i < len; i++) {
        if (flow < len) break;
        tmp = min(addflow[i], flow / len);
        ans += 1ll * tmp * i;
        flow -= 1ll * tmp * len;
    }
    cout << ans << endl;
    return 0;
}

H.kink

题意类似于求,等比数列的长度,要求长度大于等于n/2向上取整。
做法,对每个相邻的数,求公比,用map进行记录,然后中间相隔一个的求公比,然后用map记录。
对出现次数大于n/8(准确多少我也不知道)的进行计数,取最大值输出就可以了。
检查的时候我用的dp要特判1的情况。现场赛,听说有很多队被卡了,我们用的map,不知道怎么的过了,下来在gym上1s能过,牛客就不行了,可能我写得不对吧。。。

#include "bits/stdc++.h"
 
 
using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 200000 + 10;
ll mod, b[maxn];
int T, n;
ll quick(ll a, ll n, ll p) {
    a %= p;
    ll ans = 1;
    while (n) {
        if (n & 1) ans = ans * a % p;
        n >>= 1;
        a = a * a % p;
    }
    return ans;
}
 
int check(ll q) {
    unordered_map<ll, int> dp;
    int ret = 0;
    if (q == 1) {
        for (int i = n; i >= 1; i--) {
            dp[b[i]]++;
            ret = max(dp[b[i]], ret);
        }
        return ret;
    }
    for (int i = n; i >= 1; i--) {
        dp[b[i]] = 1;
        dp[b[i]] = max(dp[b[i] * q % mod] + 1, dp[b[i]]);
        ret = max(ret, dp[b[i]]);
    }
    return ret;
}
unordered_map<ll, int> mp;
 
int main() {
    T = read();
    while (T--) {
        mp.clear();
        scanf("%d %lld", &n, &mod);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &b[i]);
        }
        ll q;
        for (int i = 1; i < n; i++) {
            q = b[i + 1] * quick(b[i], mod - 2, mod) % mod;
            mp[q]++;
        }
        for (int i = 1; i < n - 1; i++) {
            q = b[i + 2] * quick(b[i], mod - 2, mod) % mod;
            mp[q]++;
        }
//        map::iterator it = mp.begin();
        int ans = 0;
        for (auto it=mp.begin(); it != mp.end(); it++) {
            if ((it->second) >= n / 8) {
                ans = max(ans, check(it->first));
            }
        }
        if (ans < (n + 1) / 2) ans = -1;
        printf("%d\n", ans);
    }
    return 0;
}

M.value

这道题,可能有的人被题意杀了,题意就是你可以选择一些下标的集合,价值为下标对于的a数组值,如果下标集合中满足任意, i > 1 , j > 1 , i k = j i>1,j>1,i^k=j i>1,j>1,ik=j则减去b[j]。问最大值。
做法就是,对于每一个进制进行枚举,然后记录个数 ,然后对此进行二进制状压暴力判断每一个进制的最大值就可以了

#include "bits/stdc++.h"


using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 100000 + 10;
const ll mod = 998244353;
int n, vis[maxn], mp[maxn];
vector<int> v[maxn];
ll a[maxn], b[maxn], ans = 0;

ll solve(int st, int sz, vector<int> l) {
    int ss = 0, p[20] = {0};
    ll ret = 0;
    for (int i = 0; i < sz; i++) {
        if ((st >> i) & 1) {
            p[++ss] = i + 1;
            ret += a[l[i]];
        }
    }
    for (int i = 1; i <= ss; i++) {
        for (int j = i - 1; j >= 1; j--) {
            if (p[i] % p[j] == 0)
                ret -= b[l[p[i] - 1]];
        }
    }
    return ret;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &b[i]);
    }
    int cnt = 0;
    for (ll i = 2; i <= n; i++) {
        if (vis[i]) continue;
        ll tmp = i;
        mp[++cnt] = i;
        while (tmp <= n) {
            vis[tmp] = 1;
            v[i].push_back(tmp);
            tmp *= i;
        }
    }
    ll ans = 0;
    for (int i = 1; i <= cnt; i++) {
        int sz = v[mp[i]].size();
        if (sz == 1) {
            ans += a[v[mp[i]][0]];
            continue;
        }
        ll tmp = 0;
        for (int j = 0; j < (1 << sz); j++) {
            tmp = max(tmp, solve(j, sz, v[mp[i]]));
        }
        ans += tmp;
    }
    ans += a[1];
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(ACM题解)