首页 > 其他分享 >ICPC2024 武汉邀请赛 题解

ICPC2024 武汉邀请赛 题解

时间:2024-05-07 13:46:28浏览次数:66  
标签:ICPC2024 int 题解 mid dep fa vector 邀请赛 dp

2024 ICPC National Invitational Collegiate Programming Contest, Wuhan Site

B - Countless Me

Solution

显然,只能执行 \(n\) 次操作是没用的条件

我们只需要把和 \(sum\) 分给 \(n\) 个数,使得 \(n\) 个数的或和最小即可

从高到低考虑每一位,假设此时枚举到第 \(i\) 位

如果这一位可以不放 \(1\),即后面的位能组成 \(sum\),则肯定不放

如果这一位要放 \(1\),考虑放几个 \(1\),显然,最大值 \(max=\min(n,\frac{sum}{2^i})\)

按照贪心的思想,放 \(max\) 个 \(1\) 即可

模拟此过程

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ll sum = 0; ll n = 0; cin >> n;
    ll ans = 0;
    for (int i = 1; i <= n; i++) { ll x; cin >> x; sum += x; }
    for (ll i = 30; i >= 0; i--) {
        if (((1ll << i) - 1) * n >= sum) continue;
        ll num = min(1ll * n, sum / (1ll << i));
        if (num == 0) continue;
        sum -= num * (1ll << i);
        ans |= 1ll <<  i;
    }
    cout << ans << '\n';
    return 0;
}

D - ICPC

Solution

定义 \(g[i][j]\) 表示从 \(i\) 点出发,向右移动不超过 \(j\) 次的和的最大值

定义 \(f[i][j]\) 表示从 \(i\) 点出发,先向左走,然后向右走的最大值,显然只会发生一次调头

不妨设在向左走了 \(l\) 步折返,那么 \(f[i][j]=\max_{l=0}^{i-1}\{g[i-l][j-l]\}\)

显然这个用前缀最大值处理就好了

对于先向右走然后折返的情况,只需要把 \(a\) 倒过来处理一次就好了

Code

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

typedef long long ll;

void calc(vector<ll> &a, vector<vector<ll> > &dp) {
    int n = a.size() - 1;
    vector<vector<ll> > g(n + 1, vector<ll>(n + 1, 0));
    for (int x = 1; x <= n; x++) {
        g[x][0] = a[x];
        for (int j = 1; j <= n; j++) {
            g[x][j] = g[x][j - 1];
            if (x + j <= n) g[x][j] += a[x + j];
        }
        
    }
    for (int s = 1; s <= n; s++)
        for (int j = 1; j <= 2 * n; j++)  {
            dp[s][j] = max(dp[s][j], max(dp[s - 1][j - 1], g[s][min(j, n)]));
        }
}   


int main() {
    int n; cin >> n;
    vector<ll> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    vector<vector<ll> > dp(n + 1, vector<ll>(2 * n + 1, 0));
    calc(a, dp);
    reverse(a.begin() + 1, a.end());
    reverse(dp.begin() + 1, dp.end());
    calc(a, dp);
    reverse(dp.begin() + 1, dp.end());
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        ll now = 0;
        for (int j = 1; j <= 2 * n; j++) 
            now ^= 1ll * j * dp[i][j];
        ans ^= (now + i);
    }
    cout << ans << '\n';
    return 0;
}

E - Boomerang

Solution

显然,我们先按照 \(r\) 为根节点建树,对于每个时刻 \(tim\),谣言传播到 \(dep\) 小于等于 \(tim\) 的节点

我们在每个时刻辟谣时,只需要观察直径,从直径的中点开始辟谣

所以我们需要维护每个时刻树的直径

有一个比较显然的结论,我们按深度从小到大加点,设深度为 \(x\) 的点在时刻 \(x\) 加入,对于一个新加入的点,它肯定时新的直径的一端,另外一端是老直径的一段

所以我们只需要维护直径的两端,对于新加入的点,用 LCA 算出新直径,看是保留那一端

求出每个时刻的直径后,对于每个 \(k=i\),我们用二分找一个时刻 \(t'\) 使得,\(k(t'-t0)\) 能覆盖整个 \(k'\) 的直径

当然这里用双指针也是可以的,因为 \(k\) 增加时,答案肯定递减

Code

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

vector<vector<int> > g;
vector<int> dep;
vector<array<int, 20> > fa;

int n, rt, t0;
int max_dep;

void dfs(int u, int f, int dp) {
    max_dep = max(max_dep, dep[u] = dp);
    fa[u][0] = f;
    for (int i = 1; i < 20; i++)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (auto &v : g[u]) {
        if (v == f) continue;
        dfs(v, u, dp + 1);
    }
}

int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 19; i >= 0; i--)
        if (dep[x] - (1 << i) >= dep[y])
            x = fa[x][i];
    if (x == y) return x;
    for (int i = 19; i >= 0; i--)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

int dis(int x, int y) { return dep[x] + dep[y] - 2 * dep[lca(x, y)]; }

int main() {
    // freopen ("E.in", "r", stdin);
    cin >> n;
    g.assign(n + 1, vector<int>());
    dep.resize(n + 1); fa.resize(n + 1);
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    cin >> rt >> t0;
    dfs(rt, 0, 0);
    vector<int> id (n + 1);
    for (int i = 1; i <= n; i++) id[i] = i;
    auto cmp = [&](int x, int y) {return dep[x] < dep[y];};
    sort (id.begin() + 1, id.end(), cmp);
    int pos = 2, max_tim = 2 * n;
    vector<int> log_dep (max_tim + 1);
    int d1 = rt, d2 = rt;
    auto insert = [&] (int x) {
        int d1_x = dis(d1, x), d2_x = dis(d2, x);
        if (d1_x > d2_x) { d2 = x; return d1_x; }
        else { d1 = x; return d2_x; }
    };

    for (int i = 1; i <= max_dep; i++) {
        while (pos <= n && dep[id[pos]] == i)
            log_dep[i] = max(log_dep[i], insert(id[pos++]));
    }
    for (int i = max_dep + 1; i <= max_tim; i++)
        log_dep[i] = log_dep[i - 1];
    for (int k = 1; k <= n; k++) {
        auto check = [&] (int t) {
            int l = log_dep[t] / 2 + (log_dep[t] & 1);
            if (1ll * k * (t - t0) >= 1ll * l) return true;
            return false;
        };
        int l = t0 - 1, r = max_tim;
        while (l + 1 < r) {
            int mid = (l + r) >> 1;
            if (check(mid)) r = mid;
            else l = mid;
        }
        cout << r << ' ';
    }
    return 0;
}

F - Custom-Made Clothes

Solution

交互题

我把题转化成求方阵中第 \(k\) 小的数 \(x\)

显然开始时二分答案,二分 \(mid\),然后去 check 在 \(mid\) 在方阵中有多少小于等于 \(mid\) 的

image.png

显然,小于等于 \(mid\) 的区域肯定是在左上角的某块区域,而有一条折的分界线

我们只需要得到这条分界线就可以得到小于等于 \(mid\) 的个数了

具体假设此时询问,\((x,y)\) 是否小于等于 \(mid\),如果返回 \(1\),则 \(y+1\),否则 \(x-1\)

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, k; cin >> n >> k;
    int l = 0, r = n * n; k = n * n - k + 1;

    auto check = [&](int x) {
        int res = 0;
        int pos_x = n, pos_y = 1;
        while (pos_x >= 1 && pos_y <= n) {
            cout << "? " << pos_x << " " << pos_y << " " << x << endl;
            int rely; cin >> rely;
            if (rely == 1) {
                res += pos_x;
                pos_y++;
            }
            else {
                pos_x--;
            }
        }
        return res;
    };

    while (l + 1 < r) {
        int mid = (l + r) / 2;
        if (check(mid) >= k) r = mid;
        else l = mid;   
    }
    cout << "! " << r << endl;
    return 0;
}

I - Cyclic Apple Strings

Solution

签到题,只需要输出除了 \(1\) 的连通块的个数 \(-1\)

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    string s; cin >> s;
    s = " " + s;
    int lst_0 = -1;
    for (int i = 1; i < s.size(); i++) 
        if (s[i] == '0')
            lst_0 = i;
    int ans = 0;
    for (int i = 1; i <= lst_0; i++) 
        if (s[i] == '1' && s[i - 1] != '1') 
            ans += 1;
    cout << ans << endl;
    return 0;
}

K - Party Games

Solution

观察 \(\oplus_{i=1}^n i\)

当 \(i\ mod\ 4 = 1\) 时,\(\oplus_{i=1}^n i=1\) ,显然先手必胜

当 \(i\ mod\ 4 = 2\) 时,\(\oplus_{i=1}^n i=n+1\) ,无论先手拿 \(1\) 还是 \(n\),后手拿另外一个,异或和位 \(0\),先手必败

当 \(i\ mod\ 4 = 3\) 时,\(\oplus_{i=1}^n i=0\) ,显然先手必胜

当 \(i\ mod\ 4 = 0\) 时,\(\oplus_{i=1}^n i=n\) ,先手拿走 \(n\) 让异或和为 \(0\),先手必胜

Code

#include <bits/stdc++.h>
using namespace std;
void solve() {
    int n; cin >> n;
    int x = n % 4;
    if (x == 0 || x == 1) puts("Fluttershy");
    else puts("Pinkie Pie");
}
int main() {
    int t; cin >> t;
    while (t--) solve();
    return 0;
}

标签:ICPC2024,int,题解,mid,dep,fa,vector,邀请赛,dp
From: https://www.cnblogs.com/martian148/p/18177060

相关文章

  • 【题解】爬山 蓝桥杯2024省B
    题目链接:P10429[蓝桥杯2024省B]拔河[蓝桥杯2024省B]拔河题目描述小明是学校里的一名老师,他带的班级共有\(n\)名同学,第\(i\)名同学力量值为\(a_i\)。在闲暇之余,小明决定在班级里组织一场拔河比赛。为了保证比赛的双方实力尽可能相近,需要在这\(n\)名同学中挑选......
  • XMOJ 四月月赛 T3 旅行 题解
    我们首先尝试挖掘这个分组的性质。我们发现,我们可以把在同一个组的夫妻和不在同一个组的夫妻分开来处理。这里,分开之后我们只需要让一种情况有顺序,另外一种不能有顺序。如果两个没有顺序/有顺序的序列合并,一定会出现漏算/多算。所以为了方便,我们可以把第二种情况看作有顺......
  • [题解]P1757 通天之分组背包
    P1757通天之分组背包分组背包模板题。总共\(s\)组,每组最多取一个物品,实际上就是一个物品总数为\(s\)的背包。for(inti=1;i<=s;i++){//枚举组 for(intj=1;j<=n[i];j++){//枚举每组的元素 for(intk=1;k<=m;k++){//枚举背包大小 f[i][k]=max(f[i][k],f[i-1][k]); if(......
  • P9527 [JOISC2022] 洒水器 题解
    题目传送门以下设\(\operatorname{dis}(x,y)\)表示树上\(x,y\)两点间的距离。修改时对\(u\)的周围与\(u\)距离小于等于\(d\)的点的点权乘\(w\)。暴力不行,于是考虑打标记。注意到\(0\led\le40\),一个很自然的想法是:设\(tag(x,i)\)表示将\(x\)的子树内与\(x\)......
  • 数数 题解
    writeby小超手123题意:现在有四种物品,分别有\(n_{1},n_{2},n_{3},n_{4}\)个,有多少种排列物品的方案使得任意两个相邻物品的种类不同。\(n_{1},n_{2}\le200,\\n_{3},n_{4}\le50000\)。分析:可以考虑先把物品\(A,B\)排列好,再把物品\(C,D\)插入进去。需要注意的......
  • P10385 艺术与篮球 题解
    一道用编程解决的数学题。大概思路是:intmonth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};这是普通年\(12\)个月的天数。然后还要考虑闰年,有\(2000,2004,2008,2012,2016,2020,2024\)。将这些闰年的二月二十九号手算出来能不能打篮球,最后加在结果上就行了。然后循环......
  • MySQL Connection not available问题解决方案
    在后端开发过程中,连接mysql数据库,过几个小时第一次使用会出现MySQLConnectionnotavailable报错这是因为MySql数据库存在一个连接池的回收时间,超过这个时间会导致资源无法正常释放,无法连接到MySql数据库1)在相关引用页面,进行定时刷新功能,这样尽管是同一个连接,但是相当于一个新......
  • 高一下三调|ssy的队列|hash dp|题解
    SSY的队列题目链接解析:考场上看到这个题第一眼是绝望的,毕竟数论咱是一窍不通.但是往下细看了看这个数据范围,N是很小的,就想了想模拟.然而只骗到10分.这个题绩效较高的解法是状压dp,在N<15的范围之内均可薄纱(ppllxx_9G也是成功拿到这70rank1了orz),可得70,但是一到后......
  • AtCoder Beginner Contest 352题解
    AtCoderBeginnerContest352Time:2024-05-04(Sat)20:00-2024-05-04(Sat)21:40AAtCoderLine问题陈述AtCoder铁路线有$N$个车站,编号为$1,2,\ldots,N$。在这条线路上,有趟进站列车从$1$站出发,依次停靠$2,3,\ldots,N$站,有趟出站列车从$N$站出发,依次停......
  • P3193 [HNOI2008] GT考试 题解
    之前学矩阵乘的时候做的题,当时因为不会\(kmp\)搜索一稀里糊涂过去了,现在填个坑。头图是\(Logos\)!P3193[HNOI2008]GT考试题链:洛谷题库题目大意:求有多少个长度为\(n\)的数字串的子串中不包含给出的长度为\(m\)位的串,范围\(n<=1e9\),$m<=20$。思路:首先考虑DP,令\(......