26.3.18 1600-1800 板刷日记

B. Shortest Statement Ever rating:1800

tag:位运算,贪心,guess

感觉 \(x,y\) 是可以至少有一个不变的,枚举不变的那个,再尝试对另一个变化,去找最小幅度变大或变小的值

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int x,y;
    cin>>x>>y;

    if((x&y)==0){
        cout<<x<<" "<<y<<endl;
        return;
    }

    vector<int> cnt(40);
    for(int i=31;i>=0;i--){
        cnt[i]+=(x>>i & 1);
        cnt[i]+=(y>>i & 1);
    }
    
    auto f=[&](int a,int b)-> vector<int> {
        int p=a,q=b;

        int pos=-1;
        for(int i=31;i>=0;i--){
            if(cnt[i]==2){
                if(pos==-1){
                    pos=i;
                }
                q^=(1<<i);
            }
        }

        for(int i=pos-1;i>=0;i--){
            if(cnt[i]==0){
                q^=(1<<i);
            }
        }
        int ans1=q,ans2=q;
        q=b;
        for(int i=pos+1;i<=30;i++){
            if(cnt[i]==0){
                q^=(1<<i);
                for(int j=i-1;j>=0;j--){
                    if(q>>j & 1) q^=(1<<j);
                }
                ans1=q;
                break;
            }
        }

        return {ans1,ans2};
    };

    int diff=inf;
    pii ans;
    for(auto val:f(x,y)){
        if(abs(y-val)<diff){
            ans={x,val};
            diff=abs(y-val);
        }
    }
    for(auto val:f(y,x)){
        if(abs(x-val)<diff){
            ans={val,y};
            diff=abs(x-val);
        }
    }

    cout<<ans.first<<" "<<ans.second<<endl;
}

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

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

G. Mixing MEXes rating:1800

tag:模拟,MEX

先处理出不变时的全局的答案,然后去枚举选择的数字

如果这个数字对某个数组会造成影响(相当于这个数是其他数组的 \(mex\)),则将这个数组 \(mex\) 的变化修改进预处理的全局答案中。

然后再看这个拿走这个数字后,会不会对原数组本身造成影响,维护这个变化

可以用 \(map\) 处理并记录下每个数组第一个 \(mex\) 和第二个 \(mex\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

vector<int> d(1e6+5);

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

    vector<int> l(n+1);
    vector<vector<int>> a(n+1);

    for(int i=1;i<=n;i++){
        cin>>l[i];
        a[i].resize(l[i]+1);
        for(int j=1;j<=l[i];j++){
            cin>>a[i][j];
        }
        sort(a[i].begin()+1,a[i].end());
    }

    int sum=0;
    vector<pii> tmp;
    vector<map<int,int>> cnt(n+1);
    vector<int> m1(n+1),m2(n+1);

    for(int i=1;i<=n;i++){
        auto &mp=cnt[i];
        for(int j=1;j<=l[i];j++){
            mp[a[i][j]]++;
        }
        int mex1=-1,mex2=-1;
        for(int val=0;;val++){
            if(mp[val]==0){
                if(mex1==-1) mex1=val;
                else{
                    mex2=val;
                    break;
                }
            }
        }
        
        tmp.push_back({mex1,mex2-mex1});
        d[mex1]+=mex2-mex1;
        sum+=mex1;
        m1[i]=mex1,m2[i]=mex2;
    }

    int ans=0;

    for(int i=1;i<=n;i++){
        for(int j=1;j<=l[i];j++){
            int add=sum*(n-1)+d[a[i][j]];
            if(m1[i]>a[i][j] && cnt[i][a[i][j]]==1) add+=(a[i][j]-m1[i])*(n-1);
            ans+=add;
        }
    }

    cout<<ans<<endl;

    for(auto [x,y]:tmp){
        d[x]-=y;
    }
}

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

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

F. BattleCows rating:1700

tag:模拟,分治思想,二进制,淘汰赛

对每个位置,最多有 \(n\) 场比赛,且每场比赛对手位置一定是固定的,知道 \(b\) 就可以算出来。

每场比赛的对手值都是用一些连续区间的数字异或起来的,且数值固定。

所以可以用前缀异或和数组,单次 \(O(1)\),一共 \(O(n)\) 的查询对手的值即可

主要是代码实现

查询区间异或值用前缀和即可,完全不用线段树,我这是一开始以为要修改,就直接拉了个线段树。但其实完全不用修改

点击查看代码
#include<bits/stdc++.h>
// #define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

class SegmentTree{
    public:
    #define lc u<<1
    #define rc u<<1|1

    struct Node{
        int l,r,sum; // sum 现在表示区间异或和
        int add;     // add 现在表示异或懒标记
    };

    int n;
    vector<int> w;
    vector<Node> tr;

    SegmentTree(int n){
        init(n);
    }

    void init(int n){
        this->n=n;
        w.resize(n+10);
        tr.resize(4*n+10);
    }

    void pushup(int u){
        tr[u].sum=tr[rc].sum^tr[lc].sum; // 加法改为异或
    }

    void pushdown(int u){
        if(tr[u].add){
            tr[rc].add^=tr[u].add; // 加法改为异或
            tr[lc].add^=tr[u].add;
            // 如果区间长度为奇数,异或和才需要更新
            if((tr[rc].r-tr[rc].l+1)&1) tr[rc].sum^=tr[u].add;
            if((tr[lc].r-tr[lc].l+1)&1) tr[lc].sum^=tr[u].add;
            tr[u].add=0;
        }
        return;
    }

    void build(int u,int l,int r){
        if(l==r) tr[u]={l,r,w[r],0};
        else{
            tr[u]={l,r};
            int mid=l+r>>1;
            build(lc,l,mid);
            build(rc,mid+1,r);
            pushup(u);
        }
    }

    int query(int u,int l,int r){
        if(l<=tr[u].l && r>=tr[u].r) return tr[u].sum;
        else{
            pushdown(u);
            int sum=0,mid=tr[u].l+tr[u].r>>1; // 0 是异或的单位元
            if(l<=mid) sum^=query(lc,l,r);    // 加法改为异或
            if(r>mid) sum^=query(rc,l,r);     // 加法改为异或
            return sum;
        }
    }

    void modify(int u,int l,int r,int k){
        if(l<=tr[u].l && r>=tr[u].r){
            // 如果区间长度为奇数,异或和才需要更新
            if((tr[u].r-tr[u].l+1)&1) tr[u].sum^=k;
            tr[u].add^=k; // 懒标记叠加改为异或
        }
        else{
            pushdown(u);
            int mid=tr[u].r+tr[u].l>>1;
            if(l<=mid) modify(lc,l,r,k);
            if(r>mid) modify(rc,l,r,k);
            pushup(u);
        }
    }
};

void solve(){// 常数优化版
    int n,q;
    cin>>n>>q;
    int k=n;

    n=(1<<n);

    vector<int> a(n+1);    
    SegmentTree tr(n);

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

    tr.build(1,1,n);

    while(q--){
        int b,c;
        cin>>b>>c;

        int ans=0;
        int val=c,pos=b;
        tr.modify(1,b,b,a[b]^c);

        for(int k1=0;k1<k;k1++){
            int tar=pos;
            if(pos&1) tar++;
            else tar--;

            int r=tar*(1<<k1);
            int l=r-(1<<k1)+1;
            int tmp=tr.query(1,l,r);

            if(tmp>val){
                ans+=(r-l+1);
            }
            else if(tmp==val){
                if((pos&1) == 0) ans+=(r-l+1);
            }

            val^=tmp;
            pos=(pos+1)/2;
        }

        cout<<ans<<endl;
        tr.modify(1,b,b,a[b]^c);
        
    }
}

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

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}


E. Exquisite Array rating:1800

tag:计数

按照 \(k\)\(1\)\(n-1\) 的顺序处理,一开始 \(k=1\) 的答案是固定的

维护一个 \(set\),初始值为 \(\{ 1,n \}\)

\(k=i\) 时,找到所有 \((a[p-1],a[p])\) 差值为 \(i\)\(p-1,p\)

相当于,在区间上,要减去当前被统计的,横跨 \(p-1,p\) 的区间

找到 \(set\) 中第一个小于等于 \(p-1\) 的位置 \(l\) 和第一个大于等于 \(p\) 的位置 \(r\)

要减去的区间数量即为 \(((p-1)-l+1)*(r-p+1)\)

在纸上画画容易想明白

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

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

    vector<int> a(n+1);
    vector<vector<pii>> d(n+1);

    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(i!=1){
            d[abs(a[i]-a[i-1])].push_back({i-1,i});
        }
    }

    set<int> st;
    st.insert(1);
    st.insert(n);
    int ans=(n-1)*n/2;

    cout<<ans<<" ";
    for(int k=1;k<n-1;k++){
        for(auto [x,y]:d[k]){
            int r=*st.lower_bound(y);
            auto it=st.upper_bound(x);

            it--;
            int l=*it;
            st.insert(x);
            st.insert(y);
            ans-=(x-l+1)*(r-y+1);
        }
        cout<<ans<<" ";
    }
    cout<<endl;
}

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

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

D. Unfair Game rating:1600

tag:二进制,组合数学

对于任何一个数,把它变成 \(0\) 的操作次数和操作方式都是固定的。

实际上就是找,对于 \(1-n\),有多少个数满足,变成 \(0\) 的操作次数 \(>k\)

转化成二进制,变成 \(0\) 的操作次数等价于,二进制位数 \(+\) 二进制中 \(1\) 的个数 \(-~1\)

\(-~1\) 是因为二进制表示是 \(0\) 时,就已经结束了

枚举二进制位数,推一些组合数即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 1e9+7;
const int N=61;
vector<int> fact(N);

void init(){
    fact[0]=fact[1]=1;
    for(int i=2;i<N;i++){
        fact[i]=i*fact[i-1]%mod;
    }
}

int qmi(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=res*a%p;
        b>>=1;
        a=a*a%p;
    }
    return res;
}


int C(int a,int b){
    if(a<b || a<0 || b<0) return 0;
    return fact[a]*qmi(fact[b],mod-2,mod)%mod*qmi(fact[a-b],mod-2,mod)%mod;
}

void solve(){
    int n,k;
    cin>>n>>k;
    int d=0;
    while((1<<d)!=n) d++;

    int ans=0;
    
    for(int i=1;i<=d;i++){
        int l=k-i+1,r=i-1;
        for(int j=l;j<=r;j++){
            ans+=C(i-1,j);
        }
    }

    //特判 n 

    if(d+1>k) ans++;

    cout<<ans<<endl;
}

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

    init();

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}

E. New Year's Gifts rating:1800

tag:贪心,认真读题!!!!!!

首先,注意 \(k\) 数值上的限制。

其次,每个礼物都是要买的。也就是说,\(y\) 是固定消耗

尽可能选择 \(z-y\) 最大的使用箱子装起来

剩下的就只能花钱买了

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

struct node{
    int x,y,z;
    bool operator<(node &t){
        return z-y>t.z-t.y;
    }
};

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

    vector<int> a(m+1);
    multiset<int> st;

    for(int i=1;i<=m;i++){
        cin>>a[i];
        st.insert(a[i]);
    }

    vector<node> p(n+1);
    for(int i=1;i<=n;i++){
        cin>>p[i].x>>p[i].y>>p[i].z;
        k-=p[i].y;
    }

    sort(p.begin()+1,p.end());

    vector<int> cost;
    int ans=0;

    for(int i=1;i<=n;i++){
        auto it=st.lower_bound(p[i].x);
        if(it!=st.end()){
            ans++;
            st.erase(it);
        }
        else{
            cost.push_back(p[i].z-p[i].y);
        }
    }

    sort(cost.begin(),cost.end());
    
    for(auto val:cost){
        if(k>=val){
            ans++;
            k-=val;
        }
        else break;
    }

    cout<<ans<<endl;
}
	
signed main(){

	ios::sync_with_stdio(0);
	cin.tie(0);

	int t=1;
	cin>>t; 
	
	while(t--){
		solve(); 
	}

}

E. Blackslex and Girls rating:1800

tag:模拟,贪心,妙妙题,代码长

先判断 \(p\) 的和是否大于 \(x+y\)

如果 \(s[i]='1'\) 则直接给 \(a[i]=p[i],b[i]=0\),反之一样

然后判断 \(a,b\) 是否大于 \(x,y\),并尝试修改(因为判断过总和,所以这里只可能有一个大于)

然后再判断 \(a,b\) 是否小于 \(x,y\),并尝试修改。

注意如果 \(s\) 中有 \(0\)\(1\),则无需判断小于。因为能加回来

如果有一个是小于的,则判断能不能加上。

如果能通过所有判断,就是 YES

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;

void solve(){
    int n,x,y;
    cin>>n>>x>>y;

    vector<int> a(n+1),b(n+1),p(n+1);
    string s;
    cin>>s;
    s=" "+s;

    for(int i=1;i<=n;i++){
        cin>>p[i];
        if(s[i]=='0') a[i]=p[i];
        else b[i]=p[i];
    }

    if(accumulate(p.begin()+1,p.end(),0ll)>x+y){
        cout<<"NO\n";
        return;
    }

    if(accumulate(a.begin()+1,a.end(),0ll)>x){
        int diff=accumulate(a.begin()+1,a.end(),0ll)-x;

        if(accumulate(b.begin()+1,b.end(),0ll)+diff>y){
            cout<<"NO\n";
            return;
        }
        for(int i=1;i<=n;i++){
            if(s[i]=='0'){
                int val=a[i]/2-(a[i]%2==0);
                if(val>diff) val=diff;
                b[i]=val;
                a[i]-=val;
                diff-=val;
                if(diff==0) break;
            }
            if(i==n){
                cout<<"NO\n";
                return;
            }
        }
    }
    else if(accumulate(b.begin()+1,b.end(),0ll)>y){
        int diff=accumulate(b.begin()+1,b.end(),0ll)-y;

        if(accumulate(a.begin()+1,a.end(),0ll)+diff>x){
            cout<<"NO\n";
            return;
        }
        for(int i=1;i<=n;i++){
            if(s[i]=='1'){
                int val=b[i]/2-(b[i]%2==0);
                if(val>diff) val=diff;
                a[i]=val;
                b[i]-=val;
                diff-=val;
                if(diff==0) break;
            }
            if(i==n){
                cout<<"NO\n";
                return;
            }
        }
    }

    bool f0=1,f1=1;
    for(int i=1;i<=n;i++){
        if(s[i]=='1') f0=0;
        else f1=0;
    }

    if(f0 && accumulate(b.begin()+1,b.end(),0ll)<y){
        //只考虑 b 是否<y
        int diff=y-accumulate(b.begin()+1,b.end(),0ll);
        for(int i=1;i<=n;i++){
            int val=a[i]-1-b[i];
            diff-=val;
            b[i]=a[i]-1;
        }
        diff-=x-accumulate(a.begin()+1,a.end(),0ll);
        if(diff>0){
            cout<<"NO\n";
            return;
        }
    }
    if(f1 && accumulate(a.begin()+1,a.end(),0ll)<x){
        //只考虑 a 是否<x
        int diff=x-accumulate(a.begin()+1,a.end(),0ll);
        for(int i=1;i<=n;i++){
            int val=b[i]-1-a[i];
            diff-=val;
            a[i]=b[i]-1;
        }
        diff-=y-accumulate(b.begin()+1,b.end(),0ll);
        if(diff>0){
            cout<<"NO\n";
            return;
        }
    }

    cout<<"YES\n";
}

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

    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
}
posted @ 2026-03-18 22:04  LYET  阅读(12)  评论(0)    收藏  举报