2026第一次周报
第四次个人赛
D. Santa's Bot
概率+数学题,算出公式即可。注意一定要边乘边取模。然后那个快速幂函数不要忘了。
int ksm(int x, int cnt){
int res=1;
x %= mod;
while(cnt>0){
if(cnt & 1){
res = res * x % mod;
}
x = x * x % mod;
cnt >>= 1;
}
return res;
}
核心作用是 高效计算 (x^cnt)%mod(x 的 cnt 次幂对 mod 取余)
还有模逆元
在模运算中,除法不直接存在(比如 (a/b) mod mod 不能直接计算),需要用逆元转化为乘法:
• 若 b 和 mod 互质,b 的逆元 inv(b) 满足 b * inv(b) ≡ 1 (mod mod);
• 此时 (a/b) mod mod = a * inv(b) mod mod。
int ans=0;
// 遍历每个集合i,计算集合内每个元素的贡献
for(int i=1;i<=n;i++){
int ki = a[i].size(); // 第i个集合的大小k_i
// 计算 inv(k_i) = 1/k_i mod mod(费马小定理求逆元)
int inv_ki = ksm(ki, mod - 2);
// 遍历集合i中的每个元素p
for(auto p:a[i]){
// 元素p的贡献 = (1/n) * (1/k_i) * c_p mod mod
// 转化为乘法:inv(n) * inv(k_i) * c_p mod mod
// 但inv(n)*inv(n) = inv(n²),所以合并为:inv(n²) * inv(k_i) * c_p mod mod
// 分步取余:避免中间结果溢出(模运算性质:(a*b*c)mod mod = ((a*b mod mod)*c) mod mod)
ans = (ans + n2 * inv_ki % mod * cnt[p] % mod) % mod;
}
}
C. Stack of Presents
最上面位置编号为1,最下面为n。不断更新过程中最下面的位置mx,如果当前就可以被mx覆盖,直接花费1秒移除即可,否则需要继续更新移动位置,并且花费代价2*(当前最深位置与此时位置的差值)+1。
for(int i=0;i<n;i++){
cin>>a[i];
p[a[i]]=i;
}
ll mx=-1;
for(int i=0;i<m;i++){
cin>>b[i];
int pos=p[b[i]];
if(pos > mx){
ans+=(2*(pos-i)+1);
mx=pos;
}
else ans++;
}
cout<<ans<<endl;
B. Verse For Santa
总结一下题意就是,找出一个最大的坐标j,使得前i个数的和减aj<=s,且i 尽可能大。推导一下就是要使得aj尽可能小,才能给剩下的数多一点空间。直接前缀和再逆序查找(保证i最大)即可。
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=pre[i-1]+a[i];
if(mx<a[i]){
mx=a[i];
pos=i;
}
c[i]=pos;
}
if(pre[n]<=s){
cout<<"0\n";
return;
}
for(int i=n;i>=1;i--){
if(pre[i]-a[c[i]]<=s){
cout<<c[i]<<endl;
return;
}
}
cout<<"0\n";
A. New Year Garland
找规律即可。让 𝑟≤𝑔≤𝑏。如果 𝑏>𝑟+𝑔+1,那么至少有两个蓝色灯会相邻,因此没有解决方案。
第三次个人赛
A. Sum of Odd Integers
前 k个奇数的和是 k^2,因此k*k<=n,且n,k要同奇偶。
B. Princesses and Princes
模拟即可,有一个女儿未婚就标记,并且输出。
for(int i=1;i<=n;i++){
cin>>a[i];
int f=0;
for(int j=1;j<=a[i];j++){
int x;cin>>x;
if(b[x]==0 && f == 0){
b[x]++;
f=1;
}
}
if(!f){
ff=i;
}
}
if(ff==0){
cout<<"OPTIMAL\n";
return;
}
cout<<"IMPROVE\n";
for(int i=1;i<=n;i++){
if(b[i]==0){
cout<<ff<<" "<<i<<endl;
break;
}
}
C. Game with Chips
根据题意知,将所有棋子移到左上角再蛇形回溯即可。
for(int i=0;i<k;++i){
int x,y;cin>>x>>y;
}
for(int i=0;i<k;++i){
int x,y;cin>>x>>y;
}
string s;
for(int i=0;i<n-1;++i)s+='U';
for(int i=0;i<m-1;++i)s+='L';
for(int i=1;i<=n;++i){
if(i%2==1){
for(int j=0;j<m-1;++j)s+='R';
}
else{
for(int j=0;j<m-1;++j)s+='L';
}
if(i!=n)s+='D';
}
cout<<s.size()<<endl<<s<<endl;
第二次个人赛
A. Berland Poker
贪心的分,使得第一个人尽可能多分到鬼牌,剩下的鬼牌平均分给剩下的人(使得差值最大),并且要向上取整,因为如果剩下的鬼牌多了,就再分给剩下的人,使得差值小了1.
ll n,k,m,x=0;cin >> n >> m >> k;
x=n/k;
if(x>=m){
cout<<m<<endl;
return;
}
m-=x;
if(m%(k-1)==0) m=m/(k-1);
else m=m/(k-1)+1;
cout<<x-m<<endl;
B. New Theatre Square
模拟即可,比较哪种方案便宜再计算。
ll n,m,x,y;cin>>n>>m>>x>>y;
ll sum=0;
for(int i=0;i<n;i++){
string s;cin>>s;
int cnt=0;
for(auto p:s){
if(p=='.')cnt++;
else{
if(2*x>y)sum+=(cnt/2)*y+(cnt%2)*x;
else sum+=x*cnt;
cnt=0;
}
}
if(cnt>0){
if(2*x>y)sum+=(cnt/2)*y+(cnt%2)*x;
else sum+=x*cnt;
}
}
cout<<sum<<endl;
C. Mixing Water
数学题,比较最优的k。
这使我们能够找到这样的 k,使得 tk的值恰好为 t。然而,这样的 k可能不是整数。(k+1)⋅h+k⋅c / (2k+1)=t ↔ k=t−h/(h+c−2t)
第一次个人赛
A. Shovels and Swords
计算单独和混造:(a+b)/3的最小值即可。
B. Shuffle
初始时ax=1,你需要执行 m次操作。在第 i次操作中,你选择两个索引 c和 d,使得 li≤c,d≤ri,并交换 ac和 ad。
请计算有多少个下标 k,可以通过选择合适的操作,使得最终 ak=1。
意思就是要求出下标k的数量,由此就是不断扩展可以交换的区间,区间的最大长度就是答案。
ll n,x,m;cin>>n>>x>>m;
ll r1=x,l1=x;
for(int i=0;i<m;i++){
ll l,r;cin>>l>>r;
if(l<=r1 && r>=l1){
l1=min(l1,l);
r1=max(r1,r);
}
}
cout<<r1-l1+1<<endl;
C. Palindromic Paths
由于这题的路径是只能从 (1,1) 往下走到 (n,m) 也就是说每走一步,横坐标加一或者纵坐标加一。那么可以很容易的发现,一个回文路径上相对应的两个点,他们的横纵坐标之和的和是不变的。
例如 (1,1) 和 (n,m) 是对应的,(1,2) 和 (n−1,m) 是对应的。可以发现总和都是 n+m+2。
也由上可见,横纵坐标和相等的点集,他们的值必定是一样的(除非这些点都是奇数长度回文的中点)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=2e5+5;
const int mod=998244353;
int a[45][45],c[65][2];
void solve(){
int n,m,ans=0;cin>>n>>m;
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin>>a[i][j];
if (a[i][j] == 0) {
c[i + j][0]++;
} else {
c[i + j][1]++;
}
}
}
if ((n + m) % 2 == 1) {
// n+m是奇数 → 矩阵中心有一个单独元素
// 只处理前半组(避免重复处理k和n+m+2-k)
for (int i = 2; i <= (n + m + 2) / 2; ++i) {
int k1 = i; // 前一个分组
int k2 = n + m + 2 - i; // 对应的对称分组
// 这两组的总元素数 = 两组0的总数 + 两组1的总数
int total = c[k1][0] + c[k1][1] + c[k2][0] + c[k2][1];
// 两种选择:全变0(需要翻转的次数=两组1的总数)、全变1(需要翻转的次数=两组0的总数)
// 选翻转次数少的 → 等价于 总元素数 - 最多的元素数(最多的元素不用翻,少的要翻)
int max_cnt = max(c[k1][0] + c[k2][0], c[k1][1] + c[k2][1]);
ans += total - max_cnt; // 累加当前组的最少翻转次数
}
} else {
// 情况2:n+m是偶数 → 没有单独元素(所有分组都成对,比如2x2矩阵,k=2和k=4成对)
// 循环范围比奇数时少1(因为中间分组k和自己对称,无需处理)
for (int i = 2; i < (n + m + 2) / 2; ++i) {
// 和奇数情况逻辑完全一样,只是循环终止条件不同
int k1 = i;
int k2 = n + m + 2 - i;
int total = c[k1][0] + c[k1][1] + c[k2][0] + c[k2][1];
int max_cnt = max(c[k1][0] + c[k2][0], c[k1][1] + c[k2][1]);
ans += total - max_cnt;
}
}
cout << ans << endl;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号