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;
}
posted @ 2026-01-24 17:57  Yxxxi  阅读(4)  评论(0)    收藏  举报