The 1st Universal Cup. Stage 20: India

Preface

久违的训练,然后选了场题目贼诡异的场,牢底坐穿了

这场最大的问题就是一个被过穿的了 Counting 一直出不来,然后 B 卡了贼久,N 帮祁神写了个特判结果还写漏了一个 Case,直接演完了

封榜后出了另一个构造,结果还是不会 G,最后看题解是个听都没听过的东西,这下是死透了


B. Minimize Median

思路很一眼的题,但一些神秘细节比较坑人,挂了好多发才艹过去

首先根据经典结论 \(\lfloor\frac{\lfloor\frac{x}{a}\rfloor}{b}\rfloor=\lfloor\frac{x}{ab}\rfloor\),我们可以先求出 \(f_i\) 表示乘积为 \(i\) 的最小代价

然后中位数问题一眼考虑二分答案 \(mid\),要检验的话需要求出将至少 \(\frac{n+1}{2}\) 个数变为 \(\le mid\) 的最小代价

贪心地想一下肯定是操作最小的 \(\frac{n+1}{2}\) 个数最优,并且如果我们要将 \(a_i\) 变为 \(mid\),最少要除的数为 \(\lfloor\frac{a_i}{mid+1}\rfloor+1\),简单处理一下即可

预处理 \(f_i\) 数组时需要注意超出 \(m\) 部分的处理,总复杂度 \(O((n+m)\log m)\)

#include <bits/stdc++.h>

using llsi = long long signed int;

llsi a[1000006], cost[1000006], dp[1000006];

llsi solve(int n, llsi mid) {
    n = (n + 1) / 2;
    llsi res = 0;
    if(mid == 0)
        for(int i = 1; i <= n; ++i) res += dp[a[i] + 1];
    else
    for(int i = 1; i <= n; ++i) {
        if(a[i] <= mid) continue;
        res += dp[a[i] / (mid + 1) + 1];
    }
    return res;
}

void work() {
    llsi n, m, k; std::cin >> n >> m >> k;
    for(int i = 1; i <= n; ++i) std::cin >> a[i];
    for(int i = 1; i <= m; ++i) std::cin >> cost[i];
    for(int i = m - 1; i >= 1; --i) cost[i] = std::min(cost[i], cost[i + 1]);
    for(int i = 2; i <= m + 1; ++i) dp[i] = 0X0D00072100114514LL;
    for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) {
        if(i * j <= m) dp[i * j] = std::min(dp[i * j], dp[i] + cost[j]);
        else {
            dp[m + 1] = std::min(dp[m + 1], dp[i] + cost[j]);
            break;
        }
    }
    for(int i = m; i >= 1; --i) dp[i] = std::min(dp[i], dp[i + 1]);
    // for(int i = 1; i <= m; ++i) std::cerr << cost[i] << char(i == m ? 10 : 32);
    std::sort(a + 1, a + n + 1);
    llsi l = 0, r = m;
    while(l < r) {
        llsi mid = (l + r) >> 1;
        if(solve(n, mid) <= k) r = mid;
        else l = mid + 1;
    }
    // std::cerr << solve(n, 3) << char(10);
    std::cout << l << char(10);
}

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) work();
    return 0;
}

D. Central Subset

队友开局讨论的,我题目都不知道

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5+5;
int n, m, deg[N], dis[N]; //dis1[N], dis2[N];
int que[N], ed=-1, fr=0;
bool vis[N];
vector<int> G[N], nG[N];

void dfs1(int x) {
    vis[x] = true;
    for (auto v : G[x]) {
        if (vis[v]) continue;
        nG[x].push_back(v);
        nG[v].push_back(x);
        ++deg[x]; ++deg[v];
        dfs1(v); 
    }
}

// int dfs2(int x, int f, int dis[]) {
//     int res = x;
//     for (auto v : nG[x]) {
//         if (v==f) continue;
//         dis[v] = dis[x] + 1;
//         int tmp = dfs2(v, x, dis);
//         if (dis[tmp] > dis[res]) res = tmp;
//     }
//     return res;
// }

void solve() {
    cin >> n >> m;
    for (int i=1; i<=n; ++i) G[i].clear(), nG[i].clear(), vis[i]=false, dis[i]=0, deg[i]=0;
    for (int i=1; i<=m; ++i) {
        int u, v; cin >> u >> v;
        G[u].push_back(v); G[v].push_back(u);
    }
    dfs1(1);
    // printf("deg:"); for (int i=1; i<=n; ++i) printf("%d ", deg[i]); puts("");

    ed=-1, fr=0;
    for (int i=1; i<=n; ++i) {
        if (1==deg[i]) que[++ed] = i;
        vis[i] = false;
    }

    int sqr = (int)sqrtl(n);
    // printf("sqr=%d\n", sqr);
    if (sqr*sqr < n) ++sqr;

    vector<int> ans;
    while (fr<=ed) {
        int x = que[fr++]; vis[x]=true;
        if (dis[x]==sqr) ans.push_back(x), dis[x]=-1;
        bool ok=false;
        for (int v : nG[x]) if (!vis[v]) {
            ok=true;
            --deg[v], --deg[x];
            dis[v] = max(dis[v], dis[x]+1);
            if (1==deg[v]) que[++ed] = v;
        }
        if (!ok) ans.push_back(x);
        // printf("x=%d vis:", x); for (int i=1; i<=n; ++i) printf("%d ", vis[i]); puts("");
        // printf("x=%d dis:", x); for (int i=1; i<=n; ++i) printf("%d ", dis[i]); puts("");
        // printf("x=%d deg:", x); for (int i=1; i<=n; ++i) printf("%d ", deg[i]); puts("");
    }
    // puts("111111");
    // dis1[1] = 0; int x = dfs2(1, -1, dis1); 
    // dis1[x] = 0; int y = dfs2(x, -1, dis1);
    // dis2[y] = 0; dfs2(y, -1, dis2);
    // printf("x=%d y=%d\n", x, y);
    // printf("dis2:"); for (int i=1; i<=n; ++i) printf("%d ", dis2[i]); puts("");

    // for (int i=1; i<=n; ++i) {
    //     if (dis1[i]+dis2[i]==dis1[y] && dis1[i]%sqr==0) ans.push_back(i);
    // }
    sort(ans.begin(), ans.end());
    ans.erase(unique(ans.begin(), ans.end()), ans.end());
    cout << ans.size() << '\n';
    for (int x : ans) cout << x << ' '; cout << '\n';
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin >> t; while (t--) solve();
    return 0;
}

F. Longest Strictly Increasing Sequence

首先注意到 \(b_i-b_{i-1}\) 的值只能是 \(0/1\),因此最后合法的整个序列一定形如 \(1,2,2,2,3,3,4,4,5\)

那么值相同的一段倒着放即可,比如上面那个例子可以放 \(1,4,3,2,6,5,8,7,9\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=15;
int t,n,a[N],ans[N],L[N],R[N];
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d",&n); 
        for (RI i=1;i<=n;++i) scanf("%d",&a[i]);
        bool flag=(a[1]==1);
        for (RI i=1;i<n;++i) if (a[i+1]-a[i]!=0&&a[i+1]-a[i]!=1) { flag=0; break; }
        if (!flag) { puts("NO"); continue; }
        for (RI i=1;i<=10;++i) L[i]=n+1,R[i]=0;
        for (RI i=1;i<=n;++i) L[a[i]]=min(L[a[i]],i),R[a[i]]=max(R[a[i]],i);
        int idx=0; for (RI i=1;i<=10;++i)
        for (RI j=R[i];j>=L[i];--j) ans[j]=++idx;
        puts("YES");
        for (RI i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
    }
    return 0;
}

K. XOR Dice

思博题

一个 naive 的想法就是每个骰子都放 \(0,d,d\times 2^6,d\times 2^{12},d\times 2^{18},d\times 2^{24}\),但这样会超出上界

不过后面徐神发现其实不用单纯的 shift,将某些情况组合在一起也是可以的,一种合法的方案是使用 \(0,d,d\times 2^6,d\times 2^{12},d\times (2^{6}+1),d\times (2^{12}+1)\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int n,d;
int main()
{
    scanf("%d%d",&n,&d);
    for (RI i=1;i<=n;++i)
    printf("%d %d %d %d %d %d\n",0,d,d*(1<<6),d*(1<<12),d*((1<<6)+1),d*((1<<12)+1));
    return 0;
}

L. (1, 2) Nim

看的博弈先 Rush 个暴力上去,打表发现以下规律:

  • 当存在 \(\ge 2\) 个非 \(1\) 的堆时,先手必败;
  • 当存在恰好一个非 \(1\) 的堆时,先手获胜当且仅当 \(n\bmod 3\ne 2\)
  • 不存在非 \(1\) 的堆时,先手获胜当且仅当 \(n\bmod 3=1\)

证明可以用归纳法,但我只能说打表 yyds

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t,n,x;
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d",&n); int cnt=0;
        for (RI i=1;i<=n;++i) scanf("%d",&x),cnt+=(x>1);
        if (cnt>=2) puts("Grundy"); else
        if (cnt==1) puts(n%3!=2?"Sprague":"Grundy");
        else puts(n%3==1?"Sprague":"Grundy");
    }
    return 0;

M. Graphs and Colors

思路很顺畅的一个题

首先由于每种颜色要将图连通,因此至少要 \(n-1\) 条边,故 \(k>\frac{n}{2}\) 时一定无解

现在考虑对 \(k=\frac{n}{2}\) 构造一种合法方案,因为对于 \(k<\frac{n}{2}\) 的情形我们可以修改其中的一些颜色为 \(1\) 来得到一个依旧合法的解

先考虑 \(n\) 为偶数的情况,将所有点两两配对,如 \((2i-1,2i)\) 间连颜色为 \(i\) 的边

对于两个点对 \(1\le i<j<\frac{n}{2}\),在 \((2i-1,2j-1),(2i,2j)\) 间连颜色为 \(i\) 的边,在 \((2i-1,2j),(2i,2j-1)\) 间连颜色为 \(j\) 的边,手玩下会发现这样一定合法

对于 \(n\) 为奇数的情况,先求出前 \(n-1\) 个点的一组合法解,然后把 \(n\) 和每个点对用对应的颜色连通即可

#include<bits/stdc++.h>
using namespace std;

const int N = 105;
int edg[N][N];

void solve() {
    int n, k; cin >> n >> k;
    if (k>n/2) {
        cout << "NO\n"; return ;
    }

    for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) edg[i][j] = 0;

    auto inv = [&](int a) {return n+1-a;};
    for (int c=1; c<=n/2; ++c) {
        edg[c][inv(c)] = c;
        for (int i=1; i<c; ++i) edg[c][inv(i)] = c, edg[i][inv(c)] = c;
        for (int i=c+1; i<=n/2; ++i) edg[c][i] = c, edg[inv(i)][inv(c)] = c;
    }
    
    if (n%2==1) {
        for (int i=1; i<=n/2; ++i) edg[i][(n+1)/2] = i, edg[(n+1)/2][inv(i)] = i;
    }

    cout << "YES\n";
    for (int i=2; i<=n; ++i) {
        for (int j=1; j<i; ++j) {
            if (edg[j][i]>k) edg[j][i] = 1;
            cout << edg[j][i] << ' ';
        }
        cout << '\n';
    }
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin >> t; while (t--) solve();
    return 0;
}

N. Red Black Grid

这场好多构造啊,贵国是构造王国吗.jpg

首先考虑特判 \(n\le 3\) 的情况,同时不难发现 \(k=1\or k=2\times n\times (n-1)-1\) 时一定无解

否则考虑进行一个棋盘染色,先假设整个棋盘都是 R,然后钦定只有 \(i+j\) 为偶数的位置能把 R 翻转成 B

此时我们相当于获得了贡献为 \(2,3,4\) 的位置,要用它们拼凑出 \(k\) 的权值,讨论下会发现先用大的再用小的,配合上一些回退操作,一定可以表示出所有情况

#include<bits/stdc++.h>
using namespace std;

using pii = pair<int, int>;

const int N = 1005;
int tbl[N][N];

void solve() {
    int n, k; cin >> n >> k;
    int tmpk = k;
    if (1==k || 2*n*(n-1) - 1 == k) {
        cout << "Impossible\n";
        return;
    }

    if (1==n) {
        if (k==0)
        {
            cout << "Possible\n";
            cout << "R\n";
        } else cout << "Impossible\n";
    } else if (2==n) {
        if (k==0)
        {
            cout << "Possible\n";
            cout << "RR\nRR\n";
        } else
        if (k==2)
        {
            cout << "Possible\n";
            cout << "BR\nRR\n";
        } else
        if (k==4)
        {
            cout << "Possible\n";
            cout << "BR\nRB\n";
        } else cout << "Impossible\n";
    } else if (3==n) {
        if (k==0)
        {
            cout << "Possible\n";
            cout << "RRR\nRRR\nRRR\n";
        } else
        if (k==2)
        {
            cout << "Possible\n";
            cout << "BRR\nRRR\nRRR\n";
        } else
        if (k==3)
        {
            cout << "Possible\n";
            cout << "RBR\nRRR\nRRR\n";
        } else
        if (k==4)
        {
            cout << "Possible\n";
            cout << "RRR\nRBR\nRRR\n";
        } else
        if (k==5)
        {
            cout << "Possible\n";
            cout << "RRB\nRRR\nRBR\n";
        } else
        if (k==6)
        {
            cout << "Possible\n";
            cout << "RBR\nRRR\nRBR\n";
        } else
        if (k==7)
        {
            cout << "Possible\n";
            cout << "RBR\nRRR\nBRB\n";
        } else
        if (k==8)
        {
            cout << "Possible\n";
            cout << "BRB\nRRR\nBRB\n";
        } else
        if (k==9)
        {
            cout << "Possible\n";
            cout << "RBR\nRRB\nRBR\n";
        } else
        if (k==10)
        {
            cout << "Possible\n";
            cout << "BRB\nRBR\nRRB\n";
        } else
        if (k==12)
        {
            cout << "Possible\n";
            cout << "BRB\nRBR\nBRB\n";
        } else cout << "Impossible\n";
    } else {
        vector<pii> pos2, pos3, pos4;
        for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) if ((i+j)%2==0) {
            if (i!=1 && i!=n && j!=1 && j!=n) pos4.push_back({i, j});
            else {
                if ((i==1 || i==n) && (j==1 || j==n)) pos2.push_back({i, j});
                else pos3.push_back({i, j});
            }
        }
        int sz2=pos2.size();
        int sz3=pos3.size();
        int sz4=pos4.size();

        vector<pii> ans;
        if (k/4 <= sz4) {
            for (int i=0; i<k/4; ++i) ans.push_back(pos4[i]);
            if (k%4 == 1) ans.pop_back(), ans.push_back(pos2[0]), ans.push_back(pos3[0]);
            else if (k%4 == 2) ans.push_back(pos2[0]);
            else if (k%4 == 3) ans.push_back(pos3[0]);
        } else {
            ans = pos4;
            if (4*sz4+1 == k) ans.push_back({1, 2});
            else {
                k -= 4*sz4;
                int bd = (n%2==0 ? 2 : 4);
                for (int i=0; i<=bd; ++i) {
                    if ((k-2*i)%3==0 && (k-2*i)/3 <= sz3) {
                        for (int j=0; j<i; ++j) ans.push_back(pos2[j]);
                        for (int j=0; j<(k-2*i)/3; ++j) ans.push_back(pos3[j]);

                        break; 
                    }
                }
            }
        }

        cout << "Possible\n";
        for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) tbl[i][j] = 0;
        for (auto [r, c] : ans) tbl[r][c] = 1;

        int tmp=0;
        for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) {
            if (i-1>=1 && tbl[i-1][j] != tbl[i][j]) ++tmp;
            if (i+1<=n && tbl[i+1][j] != tbl[i][j]) ++tmp;
            if (j-1>=1 && tbl[i][j-1] != tbl[i][j]) ++tmp;
            if (j+1<=n && tbl[i][j+1] != tbl[i][j]) ++tmp;
        }
        tmp /= 2;
        // printf("tmp=%d\n", tmp);
        assert(tmp==tmpk);

        for (int i=1; i<=n; ++i) {
            for (int j=1; j<=n; ++j) cout << (tbl[i][j] == 0 ? 'B' : 'R');
            cout << '\n';
        }
    }
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin >> t; while (t--) solve();
    return 0;
}

Postscript

感觉今天这场啥也没写就结束了,估计是起的太早导致全程犯困,看来得好好调整下生物钟了的说

posted @ 2024-09-28 16:37  空気力学の詩  阅读(96)  评论(0)    收藏  举报