湖南多校 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;
}
模拟。
#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;
}
求 \(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();
}
给定一个合法括号序列,问能否将某个前缀剪切下来拼到后面得到另一个合法括号序列(与给定的括号序列不同)
哈希判断即可。
#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;
}
诈骗题,给定一些骰子,将它们掷出的点数相加,按照每种 点数和 出现的概率从大到小输出点数。
从所有骰子的平均值之和向两边输出即可。
#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;
}
有一些青蛙在一些荷叶上,每次选择一个青蛙,它会向前跳到最近的空荷叶上;输出最终每个青蛙所在位置。
用 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;
}
在一个图上,有若干点有虫洞,虫洞会将人随机的传送到其他虫洞,求从 \(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;
}
有 \(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;
}
给定 \(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;
}
给定一个 \(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;
}

浙公网安备 33010602011771号