其他分享
首页 > 其他分享> > 训练赛 - 2020icpc澳门

训练赛 - 2020icpc澳门

作者:互联网

A B C D E F G H I J K L
1 0 2 1 0 2 2 0 0 0 0 1

0:未完成

1:赛时做出

2:赛后补

总结:

3题铁牌。比赛时负责C和G,想法的方向是正确的,但是想的过于浮于表面,没有深入地去优化。签到题常规题基本都是 复杂度高做法+使劲优化 = 正解。想好写的做法,多寻找问题所拥有的特别性质,观察数据范围有助于发现突破口。

A - Accelerator(分治fft)

即求类似\(a_1a_2a_3 + a_2a_3+a_3\)这样的式子。关键是求每一项的总和。就是每一项取与不取的,即

\[\prod_{i=1} ^{n}{(1+a_ix)} \]

然后多项式每一项的系数就是需要的值。还需要乘上每一项对应的序列前面的组合个数。

有点卡时间,可以预处理出原根的次幂,减少常数。

#include <bits/stdc++.h>
typedef long long ll;
#define endl '\n'
using namespace std;
const int N = 3e5 + 10;
const int M = 998244353;

inline ll qpow(ll a ,ll b, ll m) {
    ll res = 1;
    while(b) {
        if(b & 1) {
            res = (res * a) % m;
        }
        a = (a * a) % m;
        b >>= 1;
    }
    return res;
}

int rev[N];
void change(vector<int> &y, int len) {
    for(int i = 0; i < len; i++) {
        rev[i] = rev[i >> 1] >> 1; 
        if(i & 1) {
            rev[i] |= len >> 1;
        }
    }
    for(int i = 0; i < len; i++) {
        if(i < rev[i]) {
            swap(y[i], y[rev[i]]);
        }
    }
    return ;
}
int ngn[N];
int rgn[N];
int inv[N];

void ntt(vector<int> &y, int len, int on) {
    change(y, len);
    for(int h = 2; h <= len; h <<= 1) {
        int gn = ngn[h];
        if(on == -1) {
            gn = rgn[h];
        }
        for(int j = 0; j < len; j += h) {
            ll g = 1;
            for(int k = j; k < j + h / 2; k++) {
                int u = y[k];
                int t = g * y[k + h / 2] % M;
                y[k] = (u + t) % M;
                y[k + h / 2] = (u - t + M) % M;
                g = g * gn % M;
            }
        }
    }
    if(on == -1) {
        int iv = inv[len];
        for(int i = 0; i < len; i++) {
            y[i] = 1ll * y[i] * iv % M;
        }
    }
}

int get(int x) {
    int res = 1;
    while(res < x) {
        res <<= 1;
    }
    return res;
}

int arr[N];
int fact[N];

vector<int> solve(int l, int r) {
    if(l == r) {
        vector<int> res(2);
        res[0] = 1;
        res[1] = arr[l];
        return res;
    }
    int mid = (l + r) / 2;
    vector<int> f = solve(l, mid);
    vector<int> g = solve(mid + 1, r);
    int tdeg = f.size() + g.size() - 2;
    int len = get(tdeg + 1);
    f.resize(len, 0);
    g.resize(len, 0);
    ntt(f, len, 1);
    ntt(g, len, 1);
    for(int i = 0; i < len; i++) {
        f[i] = 1ll * f[i] * g[i] % M;
    }
    ntt(f, len, -1);
    f.resize(tdeg + 1);
    return move(f);
}

int main() {
    fact[0] = 1;
    for(int i = 1; i < N; i++) {
        fact[i] = 1ll * fact[i - 1] * i % M;
        inv[i] = qpow(i, M - 2 ,M);
    }
    for(int i = 1; i < N; i <<= 1) {
        ngn[i] = qpow(3, (M - 1) / i, M);
        rgn[i] = qpow(ngn[i], M - 2, M);
    }
    int t;
    scanf("%d", &t);
    while(t--) {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &arr[i]);
        vector<int> res = solve(1, n);
        ll ans = 0;
        for(int i = 1; i <= n; i++) {
            ans += 1ll * fact[i] * fact[n - i] % M * res[i] % M;
        }
        ans %= M;
        printf("%d\n", ans * qpow(fact[n] , M - 2, M) % M);
    }
}

C - Club Assignment(分治,暴力)

将所有数排序,然后枚举最高位,每次可以分为最高位为0/1两部分。可以发现,横跨两组的数之间异或起来一定比组内的之间异或的大,即横跨两组的数之间异或对答案没有贡献(因为不会是最小值),即两组之间没有分配的限制,不用管。于是就可以继续分成两部分处理。

一直分下去,如果都是相同的数,相同的数大于2,说明答案为0;否则将它们分到不同的集合。否则最后一定会分成小于等于4的集合。对于每个小于等于4的集合,直接暴力枚举每一种分配的组合,从中选择结果最大的分配。最后答案就是所有这些结果的最小值。

这个最优的分配方案和异或最小生成树也有关。

#include <bits/stdc++.h>
typedef long long ll;
#define endl '\n'
using namespace std;
const int N = 3e5 + 10;
const int M = 998244353;
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int, int> PII;
PII arr[N];
int ans[N];

ll solve(int l, int r, int cur) {
    if(r - l + 1 == 1) {
        ans[arr[l].second] = 1;
        return INF;
    }
    if(r - l + 1 <= 4) {
        if(r - l + 1 == 2) {
            ans[arr[l].second] = 1;
            ans[arr[r].second] = 2;
            return INF;
        }
        vector<PII> s1, s2;
        ll mx = 0;
        for(int i = l; i <= r; i++) {
            for(int j = i + 1; j <= r; j++) {
                vector<PII> a, b;
                a.push_back(arr[i]);
                a.push_back(arr[j]);
                for(int k = l; k <= r; k++) {
                    if(k == i || k == j) continue;
                    b.push_back(arr[k]);
                }
                ll va = INF, vb = INF;
                va = a.front().first ^ a.back().first;
                if(b.size() > 1) vb = b.front().first ^ b.back().first;
                if(min(va, vb) >= mx) {
                    s1 = move(a);
                    s2 = move(b);
                    mx = min(va, vb);
                }
            }
        }
        for(auto p : s1) ans[p.second] = 1;
        for(auto p : s2) ans[p.second] = 2;
        return mx;
    }
    if(cur < 0) {
        if(r - l + 1 >= 3) {
            for(int i = l; i <= r; i++) ans[arr[i].second] = 1;
            return 0;
        }
        if(r - l + 1 == 2) {
            ans[arr[l].second] = 1;
            ans[arr[r].second] = 2;
        } else {
            ans[arr[l].second] = 1;
        }
        return INF;
    }
    int p = l;
    while(p <= r) {
        if((arr[p].first & (1 << cur))) {
            break;
        }
        p++; 
    }
    ll mi = INF;
    if(p > l) mi = min(mi, solve(l, p - 1, cur - 1));
    if(p <= r) mi = min(mi, solve(p, r, cur - 1));
    return mi;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++) {
            int x;
            cin >> x;
            arr[i] = {x, i};
        }
        sort(arr + 1, arr + 1 + n);
        cout << solve(1, n, 30) << endl;
        for(int i = 1; i <= n; i++) {
            cout << ans[i];
        }
        cout << endl;
    }
}

G - Game on Sequence(暴力)

设\(f(i)=1\),代表棋子到位置\(i\)是必胜的,反之亦然。然后就有很简单的转移方程

\[f(i)=\operatorname{NOT}{(\operatorname{AND} f(j))} \]

\(j\)代表\(i\)能转移到的位置。显然这样时间复杂度太高。观察发现,如果位置\(i\)的值为\(A\),在它之后也有一个位置\(j\)值为\(A\),那么\(f(i)=1\)。因为如果\(f(j)=0\),有\(f(i)=1\);如果\(f(j)=1\),说明\(j\)之后有个位置\(k\)有\(f(k)=0\),那么就有\(f(i)=1\)。因此只需维护最后面的不同的\(A\)的对应位置的\(f\)值即可。\(A\)的值域只有255,直接暴力计算即可。时限为6s,非常充裕。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 5e5 + 10;
const int M = 500;
const double eps = 1e-5;

int pos[500];
int num[500];
int npos[500];
bitset<500> vis;
bitset<500> flag;
int arr[N];
vector<int> np[500];
int len;

void add(int x, int p) {
    if(!npos[x]) {
        npos[x] = ++len;
        num[len] = x;
    } else {
        for(int i = npos[x]; i + 1 <= len; i++) {
            num[i] = num[i + 1];
            npos[num[i]] = i;
        }
        num[len] = x;
        npos[x] = len;
    }
    pos[x] = p;
    arr[p] = x;
}

bool que(int p) {
    if(p < pos[arr[p]]) return true;
    vis.reset();
    flag.reset();
    for(int i = len; i >= 1; i--) {
        int x = num[i];
        bool ok = 0;
        for(int nt : np[x]) {
            if(vis[nt]) {
                ok |= (!flag[nt]);
            }
        }
        flag.set(x, ok);
        vis.set(x);
    }
    return flag[arr[p]];
}



bool chk(int a, int b) {
    int c = a ^ b;
    int cnt = 0;
    while(c) {
        if(c & 1) cnt++;
        c >>= 1;
    }
    return cnt <= 1;
}

int main() {
    IOS;
    for(int i = 0; i < 256; i++) {
        for(int j = 0; j < 256; j++) {
            if(chk(i, j)) np[i].push_back(j);
        }
    }
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        add(x, i);
    }
    int cur = n + 1;
    while(m--) {
        int op;
        cin >> op;
        if(op == 1) {
            int x;
            cin >> x;
            add(x, cur++);
        } else {
            int p;
            cin >> p;
            if(que(p)) cout << "Grammy" << endl;
            else cout << "Alice" << endl;
        }
    }
}

标签:arr,return,int,res,ll,澳门,len,2020icpc,训练赛
来源: https://www.cnblogs.com/limil/p/15412456.html