湖南多校6
Dashboard - 2025 Hunan Multi-School Training Round 6 - Codeforces
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;
}
const int N = 1e5 + 5;
int a[N], b[N];
void solve()
{
int n = read(), m = read(), l = read();
for(int i = 1; i <= n; ++i)
{
a[i] = read();
}
for(int i = 1; i <= m; ++i) b[i] = read();
sort(a + 1, a + n + 1);
sort(b + 1, b + m + 1);
int mx = 0;
a[0] = 0, a[n + 1] = l;
for(int i = 2; i <= n; ++i) mx = max(mx, a[i] - a[i - 1]);
int x = 0, t1 = a[1] - a[0], t2 = a[n + 1] - a[n];
for(int i = m; i >= 1; --i)
{
if(b[i] + b[i] >= mx && b[i] >= t1 && b[i] >= t2) x = i;
}
if(x == 0) printf("-1\n");
else printf("%d\n", b[x]);
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
Problem - C - Codeforces
由于每次操作都会减一半,有效的 \(\sum k\) 是 \(O(n)\) 级别,暴力模拟。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
int n, q, a[N], sta[N], tot;
int main(){
int t; cin >> t;
while(t--){
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int l = 1;
while(q--){
int k; cin >> k;
// cout << k;
for (int i = l; i <= l + k; i++) {
if ((i - l + 1) % 2 == 0) {
sta[++tot] = a[i];
}
}
int j = l + k / 2;
while(tot) {
a[j] = sta[tot--];
j++;
}
l = l + k / 2;
// cout << endl;
}
for (int i = l; i <= n ;i++) cout << a[i] << ' ';
}
}
Problem - D - Codeforces
样例就是正解,将比 \(lowbit\) 更高的位都置一,记为 \(x\),那么 \(x \oplus (x << 1)\) 即为 \(lowbit\)。
#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;
}
void solve()
{
int n = read();
int cnt = __lg(n);
printf("%d\n", 2 * cnt + 4);
for(int i = 1; i <= cnt + 1; ++i)
{
printf("B%d = B%d << %d\n", i * 2 - 1, i * 2 - 2, (1 << (i - 1)));
printf("B%d = B%d | B%d\n", i * 2, i * 2 - 1, i * 2 - 2);
}
int t = 2 * cnt + 2;
printf("B%d = B%d << 1\n", t + 1, t);
printf("B%d = B%d ^ B%d\n", t + 2, t + 1, t);
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
Problem - B - Codeforces
数位只有 \(0\sim 9\),因此数位积只有 \(2,3,5,7\) 四个质因子,枚举它们的个数,设数位积为 \(d\),其中 \(5,7\) 保留,\(2,3\) 按照 \(9,8,6,4\) 的顺序优先组合,得到的数位从小到大排序构成数 \(x\),因此 \(x\) 即为最小的能表示数位积 \(d\) 的数,预处理后二分查询。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1000000000000000000ll;
int cnt[]={54,36,18,18};
int num[]={2,3,5,7};
int st[500005],top;
int val[5];
int st2[11451],top2;
int getval()
{
top2=0;
int a0=val[0],a1=val[1];
while(a0>=3)
{
a0-=3;
st2[++top2]=8;
}
while(a1>=2)
{
a1-=2;
st2[++top2]=9;
}
if(a1>=1&&a0>=1)
{
st2[++top2]=6;
a1--;a0--;
}
if(a0==2) st2[++top2]=4;
if(a0==1) st2[++top2]=2;
if(a1==1) st2[++top2]=3;
for(int i=1;i<=val[2];++i)
{
st2[++top2]=5;
}
for(int i=1;i<=val[3];++i)
{
st2[++top2]=7;
}
if(top2>=19) return -1;
sort(st2+1,st2+1+top2);
int x=0;
for(int i=1;i<=top2;++i)
{
x=x*10ll+st2[i];
}
return max(x,1ll);
}
void dfs(int now)
{
//if(val1>18) return ;
if(now>=4)
{
int x=getval();
if(x!=-1)st[++top]=x;
return ;
}
for(int i=0;i<=cnt[now];++i)
{
val[now]=i;
dfs(now+1);
}
}
int ef(int n)
{
int l=1,r=top+1;
while(r>l)
{
int mid=(l+r)/2;
if(st[mid]>n)
{
r=mid;
}
else
{
l=mid+1;
}
}
return l-1;
}
signed main()
{
st[++top]=10;
dfs(0);
//cout<<top<<endl;
sort(st+1,st+1+top);
//for(int i=1;i<=top;++i) cout<<st[i]<<endl;
//cout<<ef(maxn)<<endl;
int t;
cin>>t;
for(int i=1;i<=t;++i)
{
int n;
cin>>n;
cout<<ef(n)<<endl;
}
return 0;
}
Problem - J - Codeforces
将 \(1\sim n\) 划分成 \(26\) 个段,每个段填入对应字符,再读入答案即可知道一个段中对应的位置的集合,对于每一个段不断划分,\(4\) 次查询足够。很码农的题。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
#define mkp make_pair
int n, q;
pair<int, int> bid[N];
int res[N];
map<pair<int, pair<int, int>>, pair<int, int>>mp;
string que(string s) {
cout << "? " << s << endl;
string ans ;
cin >> ans;
return ans;
}
int main(){
cin >> n;
for (int i = 1; i <= n; i++) {
bid[i] = mkp(1, n);
}
vector<pair<int, int>> kuai;
kuai.push_back({1, n});
for (int i = 1; i <= 4; i++) {
vector<pair<int, int>> fkuai;
string s = "";
int len = 0;
for (auto j : kuai) {
int kuail = j.first, kuair = j.second;
int num = max(1, (kuair - kuail + 26) / 26);
// cout << num << endl;
for (int k = 0; k < 26; k++) {
char x = 'a' + k;
int pnum = num;
int l = len + 1;
while(len < kuair && pnum--) {
s += x; len++;
}
int r = len;
mp[mkp(k, mkp(kuail, kuair))] = mkp(l, r);
fkuai.push_back({l, r});
// cout << kuail << ' ' << kuair << ' ' << l << ' ' << r << endl;
if (len == kuair) break;
}
}
string ans = que(s); ans = '1' + ans;
for (int i = 1; i <= n; i++) {
// cout << bid[i].first << ' ' << bid[i].second << ' ' << ans[i] - 'a' << endl;
bid[i] = mp[mkp(ans[i] - 'a', mkp(bid[i].first, bid[i].second))];
}
swap(fkuai, kuai);
// if (kuai.size() == n) break;
}
cout << "! ";
for (int i = 1; i <= n; i++) {
res[bid[i].first] = i;
}
for (int i = 1; i <= n; i++) cout << res[i] << ' ';
cout << endl;
}
Problem - F - Codeforces
正解是注意到考虑完前 \(i\) 辆车,最慢速车道的速度一定是最慢的车,于是只需要记录较快车道的速度。(优化掉无用状态)
场上没发现,但是注意到状态数可能很少,于是只记录可能成为答案的状态转移。(题解恰好证明了状态数很少)。
#include<bits/stdc++.h>
using namespace std;
int mp[5005];
int s[1005],s2[1005];
vector<int>v[2][1005];
int dp[2][1005][1005];
bool vis[2][1005][1005];
const int INF=1145141919;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>s[i];
s2[i]=s[i];
}
sort(s2+1,s2+1+n);
int m=unique(s2+1,s2+1+n)-s2-1;
//cout<<m<<endl;
for(int i=1;i<=m;++i)
{
for(int j=1;j<=m;++j)
{
dp[0][i][j]=dp[1][i][j]=INF;
}
}
dp[0][m][m]=0;
vis[0][m][m]=1;
v[0][m].push_back(m);
for(int i=1;i<=m;++i)
{
mp[s2[i]]=i;
}
int r=0;
for(int i=1;i<=n;++i)
{
r^=1;
int id=mp[s[i]];
for(int j=id;j<=m;++j)
{
for(auto k:v[r^1][j])
{
if(!vis[r][id][k]) v[r][id].push_back(k),vis[r][id][k]=1;
dp[r][id][k]=min(dp[r][id][k],dp[r^1][j][k]);
}
}
for(int j=1;j<id;++j)
{
for(auto k:v[r^1][j])
{
if(k<=id)
{
if(!vis[r][j][k]) v[r][j].push_back(k),vis[r][j][k]=1;
dp[r][j][k]=min(dp[r][j][k],dp[r^1][j][k]+s2[id]-s2[k]);
}
else
{
if(!vis[r][j][id]) v[r][j].push_back(id),vis[r][j][id]=1;
dp[r][j][id]=min(dp[r][j][id],dp[r^1][j][k]);
if(!vis[r][j][k]) v[r][j].push_back(k),vis[r][j][k]=1;
dp[r][j][k]=min(dp[r][j][k],dp[r^1][j][k]+s2[id]-s2[j]);
}
}
}
//cout<<r<<endl;
for(int j=1;j<=m;++j)
{
for(auto k:v[r^1][j])
{
dp[r^1][j][k]=INF;
vis[r^1][j][k]=0;
//cout<<s2[j]<<' '<<s2[k]<<endl;
}
//cout<<endl;
v[r^1][j].clear();
}
//cout<<endl;
}
int ans=INF;
for(int i=1;i<=m;++i)
{
for(auto k:v[r][i])
{
ans=min(ans,dp[r][i][k]);
vis[r][i][k]=0;
}
}
for(int i=1;i<=m;++i) v[1][i].clear(),v[0][i].clear();
cout<<ans<<endl;
}
}
Problem - H - Codeforces
裸的最短路,一条路径拆分成 \(O(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 = 1005;
int n, m, a, b;
vector<int> TO[N];
int depth[N], f[N];
void dfs(int k, int fa)
{
f[k] = fa, depth[k] = depth[fa] + 1;
for(auto v : TO[k])
{
if(v == fa) continue;
dfs(v, k);
}
}
int LCA(int x, int y)
{
while(x != y)
{
if(depth[x] < depth[y]) y = f[y];
else if(depth[x] > depth[y]) x = f[x];
else x = f[x], y = f[y];
}
return x;
}
int sta[N];
struct Edge
{
int v, mod, lim, k;
};
vector<Edge> to[N];
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll dis[N];
int vis[N];
priority_queue< pair<ll, int> > q;
void dijkstra(int s)
{
for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
dis[s] = 0;
q.push(pair<ll, int>(0, s));
while(!q.empty())
{
int now = q.top().second ;
q.pop();
if(vis[now]) continue;
vis[now] = 1;
for(auto [v, mod, lim, k] : to[now])
{
ll t1 = dis[now] / mod, t2 = dis[now] % mod;
if(t2 <= lim) t2 = lim;
else ++t1,t2 = lim;
if(dis[v] > t1 * mod + t2 + k)
{
dis[v] = t1 * mod + t2 + k;
q.push(pair<ll, int>(-dis[v], v));
}
}
}
}
void solve()
{
n = read(), m = read(), a = read(), b = read();
for(int i = 1; i < n; ++i)
{
int u = read(), v = read();
TO[u].emplace_back(v);
TO[v].emplace_back(u);
}
dfs(1, 0);
for(int i = 1; i <= m; ++i)
{
int s = read(), t = read(), k = read();
int x = s, y = t, lca = LCA(x, y);
int tot = depth[x] + depth[y] - depth[lca] - depth[lca] + 1, top = tot;
while(y != lca) sta[top--] = y, y = f[y];
top = 1;
while(x != lca) sta[top++] = x, x = f[x];
sta[top] = lca;
// for(int i = 1; i <= tot; ++i) printf("%d ", sta[i]);
// printf("\n");
int mod = (tot - 1) * 2 * k;
for(int i = 1; i < tot; ++i)
{
to[sta[i]].emplace_back((Edge){sta[i + 1], mod, (i - 1) * k, k});
}
for(int i = tot; i > 1; --i)
{
to[sta[i]].emplace_back((Edge){sta[i - 1], mod, (tot - 1) * k + (tot - i) * k, k});
}
}
dijkstra(a);
if(dis[b] == inf) printf("-1\n");
else printf("%lld\n", dis[b]);
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
Problem - L - Codeforces
考虑枚举矩形 \([l_x,r_x]\times [l_y,r_y]\),设 \(p\) 为选一个矩形且不覆盖枚举矩阵的概率。
考虑横坐标在 \([0,l_x]\) 或者 \([l_y,n]\),或纵坐标在 \([0,r_x]\) 或 \([r_y,m]\),再减去重复矩形。
再考虑矩形 \([l_x,r_x]\times [l_y,r_y]\) 在第 \(i\) 次被选择后不再被覆盖的概率,为 \(p^{k-i},\ \ i\in [1,k]\)。
答案为
将所有矩形累加后除以 \(\binom{n+1}{2}\times \binom{m+1}{2}\) 即可。(这是选择枚举矩阵的概率)
#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 = 55;
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b, ll mod)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
ll jc[N], inv[N];
void init()
{
jc[0] = jc[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= 50; ++i) jc[i] = jc[i - 1] * i % mod;
inv[50] = qpow(jc[50], mod - 2, mod);
for(int i = 49; i >= 2; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int n, int m)
{
if(n < 0 || m < 0 || n < m) return 0;
return jc[n] * inv[m] % mod * inv[n - m] % mod;
}
void solve()
{
int n = read(), m = read(), k = read();
ll ans = 0;
for(int lx1 = 0; lx1 < n; ++lx1)
for(int lx2 = lx1 + 1; lx2 <= n; ++lx2)
for(int rx1 = 0; rx1 < m; ++rx1)
for(int rx2 = rx1 + 1; rx2 <= m; ++rx2)
{
ll a = (C(lx1 + 1, 2) + C(n - lx2 + 1, 2)) % mod;
a = a * qpow(C(n + 1, 2), mod - 2, mod) % mod;
ll b = (C(rx1 + 1, 2) + C(m - rx2 + 1, 2)) % mod;
b = b * qpow(C(m + 1, 2), mod - 2, mod) % mod;
ll p = ((a + b - a * b) % mod + mod) % mod;
// printf("%d %d %d %d a = %lld b = %lld p = %lld\n", lx1, rx1, lx2, rx2, a, b, p);
ll t1 = (1ll - qpow(p, k, mod) + mod) % mod, t2 = qpow(1ll - p + mod, mod - 2, mod);
p = t1 * t2 % mod;
// printf("t2 = %lld\n", t2 * 2 % mod);
// printf("t1 = %lld, t2 = %lld, p = %lld\n", t1, t2, p);
ans += p;
}
printf("%lld\n", ans % mod * qpow(C(n + 1, 2) * C(m + 1, 2) % mod, mod - 2, mod) % mod);
}
int main()
{
init();
int T = 1;
while(T--) solve();
return 0;
}
Problem - K - 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 = 1e5 + 5;
int n, du[N], m, id[N];
int vis[N];
bool cmp(int x, int y){ return du[x] < du[y]; }
vector<int> to[N];
void solve()
{
n = read(), m = read();
for(int i = 1; i <= n; ++i) id[i] = i;
for(int i = 1; i <= m; ++i)
{
int u = read(), v = read();
++du[u], ++du[v];
to[u].emplace_back(v);
to[v].emplace_back(u);
}
sort(id + 1, id + n + 1, cmp);
int pos = 0;
// for(int i = 1; i <= n; ++i) printf("%d ", id[i]);
// printf("\n");
for(int i = 1; i <= n; ++i)
{
int flag = 0;
for(auto v : to[id[i]])
{
flag |= vis[v];
}
if(!flag) vis[id[i]] = 1;
else
{
pos = i;
break;
}
}
// printf("pos = %d\n", pos);
if(!pos){ printf("YES\n"); return ; }
for(int i = pos; i <= n; ++i)
{
int cnt = 0;
for(auto v : to[id[i]])
{
if(!vis[v]) ++cnt;
}
if(cnt != n - pos){ printf("NO\n"); return ; }
}
printf("YES\n");
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
Problem - A - Codeforces
牛逼线代,不会。
Problem - I - Codeforces
牛逼数论,不会。
Problem - G - Codeforces
牛逼数论,不会。

浙公网安备 33010602011771号