【AtCoder】AGC018

A - Getting Difference

我们肯定可以得到这些数的gcd,然后判断每个数减整数倍的gcd能否得到K

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
int N,K;
int a[MAXN],g;
int gcd(int a,int b) {
    return b == 0 ? a : gcd(b,a % b);
}
void Solve() {
    read(N);read(K);
    for(int i = 1 ; i <= N ; ++i) {
    read(a[i]);
    g = gcd(a[i],g);
    }
    for(int i = 1 ; i <= N ; ++i) {
    if(a[i] >= K && (a[i] - K) % g == 0) {
        puts("POSSIBLE");
        return;
    } 
    }
    puts("IMPOSSIBLE");
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

B - Sports Festival

对于初始的喜爱度答案,如果我们不把含有个数最多的比赛ban掉,答案永远不会下降,所以我们试试一次次ban掉人数最多的比赛,取每次最小值

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
int N,M;
int a[305][305],ans;
int cnt[305],pos[305];
bool vis[305];
void Solve() {
    read(N);read(M);
    for(int i = 1 ; i <= N ; ++i) {
    for(int j = 1 ; j <= M ; ++j) {
        read(a[i][j]);
    }
    }
    for(int i = 1 ; i <= N ; ++i) {cnt[a[i][1]]++;pos[i] = 1;}
    for(int i = 1 ; i <= M ; ++i) ans = max(ans,cnt[i]);
    for(int k = 1 ; k < M ; ++k) {
    int t = 1;
    for(int j = 1 ; j <= M ; ++j) {
        if(cnt[j] > cnt[t]) t = j;
    }
    vis[t] = 1;
    for(int i = 1 ; i <= N ; ++i) {
        cnt[a[i][pos[i]]]--;
        while(vis[a[i][pos[i]]]) {++pos[i];}
        cnt[a[i][pos[i]]]++;
    }
    int tmp = 0;
    for(int j = 1 ; j <= M ; ++j) {
        tmp = max(tmp,cnt[j]);
    }
    ans = min(ans,tmp);
    }
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

C - Coins

把每个金币变成\((A_{i} - C_{i},B_{i} - C{i},0)\),然后总贡献加上所有\(C[i]\)
按照\(A[i] - B[i]\)排序,然后枚举一个中间点K,在K里面选\(B[i] - C[i]\)最大的Y个,在后面选\(A[i] - C[i]\)最大的\(X\)个,取最小值

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
int X,Y,Z;
int64 A[MAXN],B[MAXN],C[MAXN],ans,f[MAXN],b[MAXN];
int id[MAXN];
multiset<int64> S;
void Solve() {
    read(X);read(Y);read(Z);
    for(int i = 1 ; i <= X + Y + Z ; ++i) {
    read(A[i]);read(B[i]);read(C[i]);
    ans += C[i];
    A[i] -= C[i];B[i] -= C[i];
    id[i] = i;
    }
    sort(id + 1,id + X + Y + Z + 1,[](int a,int b){return A[a] - B[a] < A[b] - B[b];});
    int64 all = 0;
    for(int i = 1 ; i <= X + Y + Z ; ++i) {
    S.insert(B[id[i]]);
    all += B[id[i]];
    if(S.size() > Y) {
        all -= *(S.begin());
        S.erase(S.begin());
    }
    f[i] = all;
    }
    S.clear();all = 0;
    for(int i = X + Y + Z ; i >= 1 ; --i) {
    S.insert(A[id[i]]);
    all += A[id[i]];
    if(S.size() > X) {
        all -= *(S.begin());
        S.erase(S.begin());
    }
    b[i] = all;
    }
    int64 tmp = f[Y] + b[Y + 1];
    for(int i = Y + 1; i <= Y + Z ; ++i) {
    tmp = max(tmp,f[i] + b[i + 1]);
    }
    out(ans + tmp);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

D - Tree and Hamilton Path

熟练选手直接分析每条边能否取到最大贡献
每条边最大贡献是二倍的它断开后分成子树的较小的子树大小
如果是哈密顿回路,这个贡献显然可以通过求一个重心构造出来
哈密顿路分析一下,只和起点有关,和起点逐步往子树扩大的方向走,扩大到至少大小为\(\frac{N}{2}\)时所经过的边权和,就是我们要扣除的
如果有一条边可以把子树断成\(\frac{N}{2}\),那么减少的一定是这条边,否则其他情况都要经过这条边
否则求一个重心,找和重心相连的边最小的,其他情况一定包含一条和重心相连的边

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
struct node {
    int to,next;int64 val;
}E[MAXN * 2];
int N,sumE,head[MAXN],siz[MAXN],son[MAXN],dep[MAXN],fa[MAXN];
int64 ans;
void add(int u,int v,int64 c) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    E[sumE].val = c;
    head[u] = sumE;
}
void dfs(int u) {
    siz[u] = 1;
    dep[u] = dep[fa[u]] + 1;
    for(int i = head[u] ; i ; i = E[i].next) {
    int v = E[i].to;
    if(v != fa[u]) {
        fa[v] = u;
        dfs(v);
        siz[u] += siz[v];
        son[u] = max(siz[v],son[u]);
        ans += min(siz[v],N - siz[v]) * 2 * E[i].val;
    }
    }
    son[u] = max(son[u],N - siz[u]);
}
void Solve() {
    read(N);
    int a,b;int64 c;
    for(int i = 1 ; i < N ; ++i) {
    read(a);read(b);read(c);
    add(a,b,c);add(b,a,c);
    }
    dfs(1);
    int t = 1;
    for(int i = 2 ; i <= N ; ++i) {
    if(son[i] < son[t] || (son[i] == son[t] && dep[i] > dep[t])) t = i;
    }
    if(N % 2 == 0 && son[t] == N / 2) {
    for(int i = head[t] ; i ; i = E[i].next) {
        if(E[i].to == fa[t]) ans -= E[i].val;
    }
    }
    else {
    int64 k = 0x7fffffff;
    for(int i = head[t] ; i ; i = E[i].next) {
        k = min(k,E[i].val);
    }
    ans -= k;
    }
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

E - Sightseeing Plan

问网格图中某一矩形区域到顶点的和
居然是可以\(O(1)\)统计的,感觉非常长知识

具体怎么\(O(1)\)呢

问\((0,0)\)点到\((x,y)\)点的方案数,是\(\binom{x + y}{y}\)

记为\(W(x,y)\)

就是\(\sum_{i = 0}^{y}W(x,i) = W(x + 1,y)\)

就是在选\(x,i\)往右走一步,然后就一直往上走

\(\sum_{i = 0}^{x}\sum_{j = 0}^{y} W(i,j) = W(x + 1,y + 1) - 1\)

因为横竖同理,我们可以把每个都合成\(W(i + 1,y)\),少一个\(W(0,y)\),所以-1

这样我们求一个区域的值

\(\sum_{i = x_{1}}^{x_{2}}\sum_{j = y_{1}}^{y_{2}} W(x_{2} + 1,y_{2} + 1) - W(x_{1},y_{2} + 1) - W(x{2} + 1,y_{2}) + W(x_{1},y_{1})\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define mo 974711
#define MAXN 1000005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
const int MOD = 1000000007;
const int V = 2000005;
int X[7],Y[7],ans,fac[V + 5],invfac[V + 5];

int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
void update(int &x,int y) {
    x = inc(x,y);
}
int fpow(int x,int c) {
    int res = 1,t = x;
    while(c) {
    if(c & 1) res = mul(res,t);
    t = mul(t,t);
    c >>= 1;
    }
    return res;
}
int C(int n,int m) {
    if(n < m) return 0;
    return mul(fac[n],mul(invfac[m],invfac[n - m]));
}
int W(int x,int y) {
    return C(x + y,x);
}
int way1(int x,int y) {
    int s1 = x - X[2],s2 = x - X[1];
    int t1 = y - Y[2],t2 = y - Y[1];
    return inc(inc(W(s2 + 1,t2 + 1),W(s1,t1)),MOD - inc(W(s2 + 1,t1),W(s1,t2 + 1)));
}
int way2(int x,int y) {
    int s1 = X[5] - x,s2 = X[6] - x;
    int t1 = Y[5] - y,t2 = Y[6] - y;
    return inc(inc(W(s2 + 1,t2 + 1),W(s1,t1)),MOD - inc(W(s2 + 1,t1),W(s1,t2 + 1)));
}
void Solve() {
    fac[0] = 1;
    for(int i = 1 ; i <= V ; ++i) fac[i] = mul(fac[i - 1],i);
    invfac[V] = fpow(fac[V],MOD - 2);
    for(int i = V - 1 ; i >= 0 ; --i) invfac[i] = mul(invfac[i + 1],i + 1);
    for(int i = 1 ; i <= 6 ; ++i) read(X[i]);
    for(int i = 1 ; i <= 6 ; ++i) read(Y[i]);
    for(int i = X[3] ; i <= X[4] ; ++i) {
    update(ans,mul(MOD - (i + Y[3] - 1),mul(way1(i,Y[3] - 1),way2(i,Y[3]))));
    update(ans,mul(i + Y[4],mul(way1(i,Y[4]),way2(i,Y[4] + 1))));
    }
    for(int j = Y[3] ; j <= Y[4] ; ++j) {
    update(ans,mul(MOD - (X[3] - 1 + j),mul(way1(X[3] - 1,j),way2(X[3],j))));
    update(ans,mul(j + X[4],mul(way1(X[4],j),way2(X[4] + 1,j))));
    }
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

F - Two Trees

显然如果两个点在两棵树里儿子的奇偶性不同,一定误解

否则把两棵树建出一个无向图,然后如果两个点都有偶数个儿子,那么连一条边,再建一个点,把两棵树的树根都连在上面

求一个欧拉回路,那么如果从第一棵树到第二棵树,那么这个点就是1,否则就是-1

这样的道理是什么呢。。。可以从底向上归纳,显然两棵树的叶子点有两条边,出度和入度相等,给叶子上填上相应的数,然后叶子指向父亲的相当于把+1或-1贡献给父亲,从底向上归纳也行

在学完选修2-2之后,我可以把这个解释为,进行合情推理后,发现是对的= =

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define MAXN 100005
#define eps 1e-12
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;T f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
        out(x / 10);
    }
    putchar('0' + x % 10);
}
int N;
int A[MAXN],B[MAXN],s[MAXN][2],ans[MAXN];
struct node {
    int to,next;
}E[MAXN * 10];
int head[MAXN * 2],sumE = 1;
bool vis[MAXN * 10];
void add(int u,int v) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    head[u] = sumE;
}
void euler_road(int u) {
    for(int &i = head[u] ; i ; i = E[i].next) {
        int w = i;
        if(!vis[w]) {
            vis[w] = vis[w ^ 1] = 1;
            int v = E[w].to;
            if(u != 2 * N + 1 && v != 2 * N + 1 && abs(u - v) == N) {
                if(u < v) ans[u] = 1;
                else ans[v] = -1;
            }
            euler_road(v);
        }
    }
}
void Solve() {
    read(N);
    for(int i = 1 ; i <= N ; ++i) {
        read(A[i]);
        if(A[i] != -1) {
            add(i,A[i]);add(A[i],i);
            s[A[i]][0]++;
        }
        else {
            add(i,2 * N + 1);add(2 * N + 1,i);
        }
    }
    for(int i = 1 ; i <= N ; ++i) {
        read(B[i]);
        if(B[i] != -1) {
            add(i + N,B[i] + N);add(B[i] + N,i + N);
            s[B[i]][1]++;
        }
        else {
            add(i + N,2 * N + 1);add(2 * N + 1,i + N);
        }
    }
    for(int i = 1 ; i <= N ; ++i) {
        if((s[i][0] ^ s[i][1]) & 1) {
            puts("IMPOSSIBLE");
            return;
        }
        if(s[i][0] % 2 == 0) {
            add(i,i + N);add(i + N,i);
        }
    }
    puts("POSSIBLE");
    euler_road(1);
    for(int i = 1 ; i <= N ; ++i) {
        out(ans[i]);space;
    }
    enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}
上一篇:c语言中实现矩阵的转置


下一篇:P1291-添加括号(区间dp)