湖南多校 10

Dashboard - The 2025 Hunan Multi-School Programming Training Contest, Round 10 - Codeforces
Problem - M - Codeforces

给定 \(d\),构造 \(a,b,c\) 使得不能用加减乘除组合出 \(d\)
手玩一下即可。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

int a,b,c,d;
void solve()
{
    cin>>d;
    if(d>4&&d+4<=100)
    {
        cout<<d+2<<' '<<d+3<<' '<<d+4<<endl;
    }
    else if(d==3)
    {
        cout<<11<<' '<<9<<' '<<4<<endl;
    }
    else if(d==2)
    {
        cout<<3<<' '<<9<<' '<<100<<endl;
    }
    else if(d==1)
    {
        cout<<3<<' '<<9<<' '<<100<<endl;
    }
    else if(d == 4)
    {
        printf("98 99 100\n");
    }else
    {
        cout<<"1 2 3"<<endl;
    }
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - E - Codeforces

模拟。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

char s[100];
char t[100][100];
char temp[100];
int len;
int tot, cnt;

void dfs(int dep)
{
    if(dep > len)
    {
        ++cnt;
        for(int i = 1; i <= tot; ++i) t[cnt][i] = temp[i];
        return ;
    }
    if(s[dep] != 'S')
    {
        ++tot;
        temp[tot] = s[dep] - 'A' + 'a';
        dfs(dep + 1);
        --tot;
    }else
    {
        ++tot;
        temp[tot] = s[dep] - 'A' + 'a';
        dfs(dep + 1);
        --tot;
        if(dep > 1 && s[dep - 1] == 'S' && temp[tot] == 's')
        {
            char c = temp[tot];
            --tot;
            temp[++tot] = 'B';
            dfs(dep + 1);
            temp[tot] = c;
        }
    }
}

void solve()
{
    scanf(" %s", s + 1);
    len = strlen(s + 1);
    dfs(1);
    for(int i = 1; i <= cnt; ++i)
    {
        int len = strlen(t[i] + 1);
        for(int j = 1; j <= len; ++j) printf("%c", t[i][j]);
        printf("\n");
    }
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - G - Codeforces

\(0\)\(n\) 中,每个数位在一个数中出现的最大次数,求和。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
void solve(){
  ll n;cin>>n;
  ll c=n;
  ll cnt=0;
  while(c>0){
    ++cnt;
    c/=10;
  }
  ll ans=10*(cnt-1);
  for(ll i=1;i<=9;++i){
    ll g=0;
    for(int j=cnt-1;j>=0;--j){
      g+=i*pow(10,j);
    } 
    if(n>=g) ++ans;
    else break;
  }
  if(cnt==1) ++ans;
  cout<<ans;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    solve();
}

Problem - L - Codeforces

给定一个合法括号序列,问能否将某个前缀剪切下来拼到后面得到另一个合法括号序列(与给定的括号序列不同)
哈希判断即可。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e6 + 5;
char s[N];
const ull Base = 13331;
ull B[N];

void solve()
{
    scanf(" %s", s + 1);
    int len = strlen(s + 1);
    for(int i = 1; i <= len; ++i) s[i + len] = s[i];
    for(int i = 1; i <= len + len; ++i) B[i] = B[i - 1] * Base + s[i];
    ull flag = B[len], BBB = 1;
    for(int i = 1; i <= len; ++i) BBB = BBB * Base;
    int cnt = 0;
    for(int i = 1; i <= len; ++i)
    {
        if(s[i] == '(') ++cnt;
        else --cnt;
        if(cnt == 0)
        {
            ull t = B[i + len] - B[i] * BBB;
            if(t == flag) continue;
            else
            {
                // printf("i = %d\n", i);
                for(int j = i + 1; j <= i + len; ++j)
                {
                    printf("%c", s[j]);
                }
                return ;
            }
        }
    }
    printf("no\n");
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - D - Codeforces

诈骗题,给定一些骰子,将它们掷出的点数相加,按照每种 点数和 出现的概率从大到小输出点数。

从所有骰子的平均值之和向两边输出即可。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}
const int N = 505;
int a[10], b[10] = {0, 4, 6, 8, 12, 20};
int id[N], A[N];
int sum, tot;
void solve()
{
    sum = 0;
    for(int i = 1; i <= 5; ++i) a[i] = read(), sum += a[i] * (b[i] + 1), tot += a[i];
    if(sum & 1)
    {
        int l = sum / 2, r = l + 1;
        for(; l >= tot || r <= sum - tot; --l, ++r)
        {
            if(l >= tot)
            {
                printf("%d ", l);
            }
            if(r <= sum - tot)
            {
                printf("%d ", r);
            }
        }
    }else
    {
        printf("%d ", sum / 2);
        for(int l = sum / 2 - 1, r = sum / 2 + 1; l >= 1 || r <= sum - tot; --l, ++r)
        {
            if(l >= 1 && l >= tot)
            {
                printf("%d ", l);
            }
            if(r <= sum - tot)
            {
                printf("%d ", r);
            }
        }
    }
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - I - Codeforces

有一些青蛙在一些荷叶上,每次选择一个青蛙,它会向前跳到最近的空荷叶上;输出最终每个青蛙所在位置。

set 维护空荷叶的位置,每次在 set 上用 lower_bound,同时记录每个青蛙的位置。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
bool a[1210000];
int pos[200010];
void solve(){
  int n;cin>>n;
  for(int i=1;i<=n;++i){
    cin>>pos[i];
    a[pos[i]]=true;
  }
  int q;cin>>q;
  vector<int> v;
  v.reserve(1210000);
  for(int i=1;i<1210000;++i){
    if(!a[i]) v.push_back(i);
  }
  set<int> s(v.begin(),v.end());
  while(q--){
    int x;cin>>x;
    int tp=pos[x];
    pos[x]=*s.lower_bound(tp);
    cout<<pos[x]<<'\n';
    s.erase(pos[x]);
    s.insert(tp);
  }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    solve();
    return 0;
}

Problem - C - Codeforces

在一个图上,有若干点有虫洞,虫洞会将人随机的传送到其他虫洞,求从 \(1\)\(n\),最多走一次虫洞时,期望走过的最短路径。

倒序求出终点到每个虫洞的距离,那么从一个虫洞传送后期望走过的路径可以 \(O(1)\) 计算,再正序求出起点到每个虫洞的距离,求出在哪个点走虫洞期望走的路径最短,最后与不走虫洞比较。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int inf = 0x3f3f3f3f;
const int N = 2e5 + 5;
int n, m, K;
int vis[N];
ll sum;
vector<int> to[N];

int dis[N];

void BFS()
{
    queue<int> q;
    for(int i = 1; i <= n; ++i) dis[i] = inf;
    dis[n] = 0;
    q.push(n);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        for(auto v : to[now])
        {
            if(dis[v] > dis[now] + 1)
            {
                dis[v] = dis[now] + 1;
                q.push(v);
            }
        }
    }
}

int dis2[N];

void BFS2()
{
    for(int i = 1; i <= n; ++i) dis2[i] = inf;
    queue<int> q;
    dis2[1] = 0;
    q.push(1);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        for(auto v : to[now])
        {
            if(dis2[v] > dis2[now] + 1)
            {
                dis2[v] = dis2[now] + 1;
                q.push(v);
            }
        }
    }
}

void solve()
{
    n = read(), m = read(), K = read();
    for(int i = 1; i <= K; ++i)
    {
        int x = read();
        vis[x] = 1;
    }
    for(int i = 1; i <= m; ++i)
    {
        int u = read(), v = read();
        to[u].emplace_back(v);
        to[v].emplace_back(u);
    }
    BFS();
    for(int i = 1; i <= n; ++i) if(vis[i]) sum += dis[i];
    BFS2();
    double ans = dis[1];
    int id = 0;
    for(int i = 1; i <= n; ++i)
    {
        if(vis[i])
        {
            if(dis2[i] + 1.0 * (sum - dis[i]) / (K - 1) < ans)
            {
                ans = dis2[i] + 1.0 * (sum - dis[i]) / (K - 1);
                id = i;
            }
        }
    }
    if(id == 0) printf("%d/1\n", dis[1]);
    else
    {
        ll t1 = 1ll * dis2[id] * (K - 1) + (sum - dis[id]);
        ll t2 = (K - 1);
        ll g = __gcd(t1, t2);
        printf("%lld/%lld\n", t1 / g, t2 / g);
    }
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - K - Codeforces

\(n\) 个骑士和 \(h\) 个家族,每个家族都有 \(k_i\) 个骑士,一个骑士最多在一个家族,也可能不在,如果在骑士排行上,前 \(i\) 个家族的骑士在后 \(k_1+k_2\cdots k_i\) 名,那么就会造反,求有多少种骑士排行,使得没有造反事件。

枚举最大的 \(i\) 使得前 \(i\) 个家族造反,方案数为 \(sum_i!\),记 \(f_{i+1}\) 为前 \(i\) 个家族造反的情况下, \(i+1\sim h\) 的家族不造反的方案数。求 \(f_{i+1}\) 也可以枚举最大的 \(j\)\(i+1\le j\),使得 \(i+1\sim j\) 的家族造反,后面的家族不造反,暴力容斥求出。

代码实现将 \(k\) 翻转,按照思路正序解决问题。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ll mod = 1e9 + 7;
const int N = 5005;
int n, h;
int k[N], sum[N];
ll jc[1000005], f[N];

void solve()
{
    n = read(), h = read();
    for(int i = h + 1; i >= 2; --i) k[i] = read();
    for(int i = 2; i <= h + 1; ++i) sum[i] = sum[i - 1] + k[i];
    sum[1] = k[1] = n - sum[h + 1];
    for(int i = 2; i <= h + 1; ++i) sum[i] += k[1];
    jc[0] = jc[1] = 1;
    for(int i = 2; i <= 1000000; ++i) jc[i] = jc[i - 1] * i % mod;
    if(k[1] == 0)
    {
        printf("0\n");
        return ;
    }
    // for(int i = 1; i <= h + 1; ++i) printf("%d ", sum[i]);
    // printf("\n");
    // return ;
    f[1] = jc[k[1]];
    for(int i = 2; i <= h + 1; ++i)
    {
        f[i] = jc[sum[i]];
        for(int j = 2; j <= i; ++j)
            f[i] -= f[j - 1] * jc[sum[i] - sum[j - 1]] % mod;
        f[i] = (f[i] % mod + mod) % mod;
    }
    printf("%lld\n", f[h + 1]);
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - B - Codeforces

给定 \(n\) 个点,判断是否可以用不超过 \(3\) 条直线覆盖所有点。

以下令 \(k=3\)

解法一:

对于用 \(k\) 条直线覆盖所有点的问题,随机找 \(k+1\) 个点,必有一条直线经过其中两点,枚举那一条直线并删去在该直线上的点,将问题转化为用 \(k-1\) 条直线覆盖所有点,递归处理,复杂度为 \(C_{4}^{2}\times C_{3}^{2}\times C_{2}^{2}\ n=18n\)

解法二:

对于用 \(k\) 条直线覆盖所有点的问题,随机取两个点,判断它们所确定的直线是否经过超过 \(\frac{n}{k}\) 个点,如果超过 \(\frac{n}{k}\) 就删去在该直线上的点,问题转化为用 \(k-1\) 条直线覆盖所有点。当题目有解时,随机两个点通过判断的概率为 \(C_{\frac{n}{k}}^{2}/C_{n}^{2}\),当 \(k=3\) 时这个值大约为 \(9\)

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 1e4 + 5;
int n;
ll X[N], Y[N];
int v1[N], v[5][N];
int flag;

void check()
{
    for(int i = 1; i <= n; ++i)
    {
        if(v1[i] == 0) return ;
    }
    flag = 1;
}

bool line(int i, int j, int k)
{
    return (Y[j] - Y[i]) * (X[k] - X[j]) == (X[j] - X[i]) * (Y[k] - Y[j]);
}

void dfs(int dep)
{
    if(flag) return ;
    if(dep == 4){ check(); return ; }
    int sta[10] = {0}, top = 0;
    for(int i = 1; i <= n; ++i)
    {
        if(!v1[i])
        {
            sta[++top] = i;
        }
        if(top == (5 - dep)) break;
    }
    if(top <= 1)
    {
        flag = 1;
        return ;
    }
    for(int i = 1; i <= top; ++i)
        for(int j = i + 1; j <= top; ++j)
        {
            for(int k = 1; k <= n; ++k) v[dep][k] = v1[k];
            for(int k = 1; k <= n; ++k) if(line(sta[i], sta[j], k)) v1[k] = 1;
            dfs(dep + 1);
            for(int k = 1; k <= n; ++k) v1[k] = v[dep][k];
        }
}

void solve()
{
    n = read();
    for(int i = 1; i <= n; ++i) X[i] = read(), Y[i] = read();
    if(n <= 6)
    {
        printf("possible\n");
        return ;
    }
    dfs(1);
    if(flag) printf("possible\n");
    else printf("impossible\n");
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - F - Codeforces

给定一个 \(w\times h\) 的墙,在每一列已经有 \(a_i\) 的高度,问能否只用 \(1\times 2\) 的砖块和 \(2\times 1\) 的砖块恰好覆盖 \(w\times h\) 的范围。

从左往右,从下往上,一次填入 \(1\times 2\) 的砖块,如果某一列上面剩一个 \(1\times 1\) 的位置,填一个 \(2\times 1\) 的砖块,只需要记录来自前一列的位置最低的砖块。当考虑完 \(w\) 后,要求没有来自前一列的砖块。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int w, h, a[N];

void solve()
{
    w = read(), h = read();
    for(int i = 1; i <= w; ++i)
    {
        a[i] = read();
        if(a[i] > h)
        {
            printf("impossible\n");
            return ;
        }
    }
    int last = h + 1;
    for(int i = 1; i <= w; ++i)
    {
        if(last <= a[i])
        {
            printf("impossible\n");
            return ;
        }
        if((last - a[i] - 1) % 2 == 0) last = min(h + 1, last + 1);
        else --last;
    }
    if(last != h + 1)
    {
        printf("impossible\n");
        
    }else printf("possible\n");
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - H - Codeforces
神秘随机化。

有一个 \(2\times 200\) 的空地,一辆车的空间是 \(1\times 2\) 或者 \(2\times 1\),请找出一种停车方案,使得剩余空地的停车方案数在模 \(10^9+7\) 的情况下为 \(n\)

注意到使用 \(1\times 2\) 的车纯占地方,所以只使用 \(2\times 1\) 的车,此时一个 \(2\times x\) 的空地的停车方案数为 \(fib_x\)

神秘双向搜索:

随机以下操作 \(a\) 次:随机一个多重集 \(S_a\),使得 \(\sum_{x\in S_i}<100\),记录 \(f(S_a)=\prod_{x\in S_i}fib_x\)

随机以下操作 \(b\) 次:随机一个多重集 \(S_b\),使得 \(\sum_{x\in S_i}<100\),记录 \(f(S_b)=\prod_{x\in S_i}fib_x\)

判断是否有 \(S_a,S_b\) 满足 \(f(S_a)\times f(S_b)\equiv n \bmod 10^9+7\)

\(a=b=10^6\) 时,我们可以测试 \(10^{12}\) 种组合,而值域只有 \(10^9+7\)

特判当 \(n=0\) 时,构造一个无解的图。

Problem - J - Codeforces ^d5ebb0

当知道了最后的排列后,总有方案使得在 n-置换环个数 情况下还原成 \(1\)\(n\)

如何维护排列:发现在每一层的横线相当于上一层到下一层的一个映射,因此以高度为下标,用线段树维护映射关系即可。

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

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int w, h, q;

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int p[N << 2][23];

void pushup(int k)
{
    for(int i = 1; i <= w; ++i) p[k][i] = p[ls(k)][p[rs(k)][i]];
}

void build(int k, int l, int r)
{
    for(int i = 1; i <= w; ++i) p[k][i] = i;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(ls(k), l, mid), build(rs(k), mid + 1, r);
    pushup(k); 
}

void update(int k, int l, int r, int t, int x, int y)
{
    if(l == r)
    {
        swap(p[k][x], p[k][y]);
        return ;
    }
    int mid = (l + r) >> 1;
    if(t <= mid) update(ls(k), l, mid, t, x, y);
    else update(rs(k), mid + 1, r, t, x, y);
    pushup(k);
}

int f[25];
int find(int x){ return x == f[x] ? (x) : (f[x] = find(f[x])); }

void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return ;
    f[y] = x;
}

void check()
{
    for(int i = 1; i <= w; ++i) f[i] = i;
    for(int i = 1; i <= w; ++i) merge(i, p[1][i]);
    int cnt = 0;
    for(int i = 1; i <= w; ++i) 
    {
        find(i);
        cnt += (i == f[i]);
    }
    printf("%d\n", w - cnt);
}

void solve()
{
    w = read(), h = read(), q = read();
    build(1, 1, h);
    while(q--)
    {
        int y = read(), x1 = read(), x2 = read();
        update(1, 1, h, y, x1, x2);
        check();
    }
}

int main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}
posted @ 2025-05-18 17:02  梨愁浅浅  阅读(79)  评论(0)    收藏  举报