[ICPC2024 Xi'an I] ICPC2024 邀请赛西安站重现赛 / The 2024 ICPC China Shaanxi National Invitational Programming Contest

M. Chained Lights

参考洛谷某题解

首先,进行偶数次完全相同的操作相当于没做。

press[1] 单独拎出来:

light[1]^=1;
for(int i=2;i<=n;i++){
  press(i);
}

然后,会进行:

for(int i=2;i<=n;i++){
  press(i);
}

会发现,只有第一个位置是 1,其他都是 0

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int n,k;
    cin>>n>>k;

    cout<<((k==1)?"YES":"NO");
    cout<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    cin>>ct;
    while(ct--) solve();

    return 0;
}

J. Triangle

对每一行去二分一个位置就好了,需要推一下数学式子

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int a,b;
    cin>>a>>b;

    int ans=0;
    
    for(int i=0;i<a;i++){
        double low=i,high=i+1;

        auto check=[&](int x)->bool {
            double l=x-1,r=x;
            if(x==0) return 1;
            double y1=((-a)*l+a*b)/b;
            double y2=((-a)*r+a*b)/b;

            if(y1<=low) return 0;
            if(y1>=high){
                if(y2>=low) return 1;
            }
            if(y1>=low && y1<=high && y2<=low) return 0;

            double x1=b*(a-high)/a;
            double x2=b*(a-low)/a;

            return ((x1+x2-2*l)/2ll>=0.5);
        };

        int l=0,r=b;
        while(l<r){
            int mid=l+r+1>>1;
            if(check(mid)) l=mid;
            else r=mid-1;
        }
        ans+=r;
    }

    cout<<ans;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

F. XOR Game

本质还是奇偶数博弈。

最关键的点:对于大于等于 2 的位置,归属一定是固定的。

所以两人只会轮流去拿等于 1 的位置

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int k,z;
    cin>>k>>z;

    vector<int> ans(k+1),b(k+1);
    int sum=z;
    for(int i=1;i<=k;i++){
        cin>>b[i];
        sum+=b[i];
    }

    b[0]=z;
    vector<int> t;

    for(int i=0;i<=k;i++){
        if(b[i]==0) continue;
        if(b[i]==1){
            t.push_back(i);
        }
        else{
            if(sum&1) ans[i]=1;
            else ans[i]=0;
        }
    }

    while(t.size()){
        auto p=t.back();
        t.pop_back();
        ans[p]=1;
        if(t.size()) t.pop_back();
    }

    for(int i=k;i>=1;i--){
        cout<<ans[i];
    }

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

D. Make Them Straight

Tag: 优化枚举,调和级数

枚举公差 \(k\),对于每一个位置,可以根据 \(k\) 反推出一个 \(a[1]\),这意味着,如果把 \(a[1]\) 修改为这个位置反推出的 \(a[1]\),则这个位置不用变化,否则这个位置需要变化。

对于每一个 \(k\),可以得到很多个反推出的 \(a[1]\),枚举这些 \(a[1]\),计算答案。

计算 \(a[i]\) 的公式是:\(a[i]-(i-1)*k\),而当 \((i-1)*k\) 大于 \(1e6\) 时,结果一定非法,此时可以结束本次 \(k\) 的循环

对于 \(k=i\) 时,循环会进行 \(1e6/i\) 次,所以是个调和级数的复杂度

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;

void solve(){
    int n;
    cin>>n;

    vector<int> a(n+1),b(n+1),sufb(n+2);
    int sum=0;

    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    for(int i=1;i<=n;i++){
        cin>>b[i];
        sum+=b[i];
    }
    for(int i=n;i>=1;i--){
        sufb[i]=sufb[i+1]+b[i];
    }

    int ans=inf;

    //当公差是时,如果首项是 i,则代价是 sum-t[i]
    vector<int> t(1e6+1);
    for(int k=0;k<=1e6;k++){
        int must=0;

        for(int i=1;i<=n;i++){
            if((i-1)*k>1e6){
                break;
            }
            int now=a[i]-(i-1)*k;
            if(now<0) continue;
            else t[now]+=b[i];
            ans=min(ans,sum-t[now]);
        }

        for(int i=1;i<=n;i++){
            if((i-1)*k>1e6){
                break;
            }
            int now=a[i]-(i-1)*k;
            if(now<0) continue;
            else t[now]-=b[i];
        }
    }

    cout<<ans<<endl;


}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

L. Rubbish Sorting

对于一个长度的为 \(5\) 的字符串,每个为有 \(0\)\(a-z\)\(27\) 个选择,所以最多有 \(27^5≈1.5e7\) 种不同的字符串

对于每个操作 \(1\),假设字符串的长度为 \(n\), 对于每个位置可以选或不选,最终得到 \(2^n\) 个字符串,然后用当前的类型去更新这这些字符串的类型

对于每个操作 \(1\),假设字符串的长度为 \(n\), 对于每个位置可以选或不选,最终得到 \(2^n\) 个字符串,枚举这些字符串,找一个最大相似度且类型最小的即可

点击查看代码
#include<bits/stdc++.h>
// #define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e9+7;;
const int mod = 1e9+7;

const int N=2e7;
vector<int> type(N,inf);

void solve(){
    int q;
    cin>>q;

    while(q--){
        int ch,x;
        string s;
        cin>>ch>>s;

        if(ch==1){
            cin>>x;
         
            for(int i=0;i<32;i++){
                int pos=0,now=1;

                for(int j=0;j<s.size();j++){
                    int val=0;
                    if((i>>j) & 1) val=s[j]-'a'+1;
                    else val=0;

                    pos+=val*now;
                    now*=27;
                }

                type[pos]=min(x,type[pos]);
            }
        }
        else{
            int sim=-1,ans=0;

            for(int i=0;i<32;i++){
                int pos=0,now=1,cnt=0;

                for(int j=0;j<s.size();j++){
                    int val=0;
                    if((i>>j) & 1){
                        val=s[j]-'a'+1;
                        cnt++;
                    }
                    else val=0;

                    pos+=val*now;
                    now*=27;
                }
                if(type[pos]==inf) continue;

                if(cnt>sim){
                    sim=cnt;
                    ans=type[pos];
                }
                else if(cnt==sim){
                    ans=min(ans,type[pos]);
                }
            }

            cout<<ans<<endl;
        }
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}

I. Smart Quality Inspector

tag: 状压DP

对于 \(k\) 个数,从大到小去填。

\(f[mask]\)表示,此时已经从大到小填了 \(popcount(mask)\) 个数,这些数填的位置压缩成二进制是 \(mask\),此时的最小值是多少

转移时,可以枚举 \(mask\) 中的每一个是 \(1\) 的位置, 从去掉这一位的 \(f\) 转移过来。

设这个位置是 \(p\),设 \(l,r\) 分别为在当前填法 \(mask\) 中,左右第一个已经被填的数 (在 \(mask\) 的二进位中,左右第一个是 1 的位置)

因为是从大到小去填数,所以将本次的数填到位置 \(p\) 后,仅仅会对完全在 \((l,r)\) 范围内,且跨越 \(p\) 的区间产生贡献。 这个东西可以通过一个类似二维前缀和的形式预处理出来

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long; 
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;

int pct(int x){
    return __builtin_popcount(x);
}

void solve(){
    int n,k,m;
    cin>>n>>k>>m;
    
    vector<vector<int>> seg(n+1,vector<int>(n+1));
    
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        a--,b--;
        seg[a][b]++;
    }

    for(int len=2;len<=n;len++){
        for(int i=0;i+len-1<n;i++){
            int j=i+len-1;
            seg[i][j]+=seg[i+1][j]+seg[i][j-1]-seg[i+1][j-1];
        }
    }

    //f[mask]:填数方式是 mask ( popcnt(mask)<=k ) 时,的最小值
    vector<int> f(1<<n,inf);
    f[0]=0;

    int ans=inf;

    auto cal=[&](int l,int r)->int {
        //计算有多少个区间落在 [l,r] 中
        if(l > r || l < 0 || r >= n) return 0;
        return seg[l][r];
    };

    for(int i=1;i<(1<<n);i++){
        if(pct(i)>k) continue;

        for(int p=0;p<n;p++){
            if((i>>p) & 1){
                int prestate=f[i^(1<<p)];
                int l=p,r=p;
                while(l>0 && (i>>(l-1) & 1) == 0){
                    l--;
                }
                while(r<n-1 && (i>>(r+1) & 1) == 0){
                    r++;
                }
                int val=k-pct(i) +1;
                //g[l,r] - g[l,p-1] - g[p+1,r]
                f[i]=min(f[i],prestate+val*(cal(l,r)-cal(l,p-1)-cal(p+1,r)));
            }
        }

        if(pct(i)==k) ans=min(ans,f[i]);
    }

    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int ct=1;
    // cin>>ct;
    while(ct--) solve();

    return 0;
}
posted @ 2026-03-09 21:26  LYET  阅读(5)  评论(0)    收藏  举报