Codeforces Round 1071 (Div. 3) A-E

A. Blackslex and Password

让位置 \(1,1+x,1+2x...,1+(k-1)x\) 用完前 \(k\) 个字母,则位置 \(1+kx\) 的字母必定要和前面那几个位置重复,所以答案就是 \(k*x+1\)

B. Blackslex and Showering

可以先计算出删数之前的答案 \(sum\)

然后可以 \(for\) 循环枚举删除第 \(i\) 个数

此时,删除这个数对答案造成的贡献是:\(-abs(a[i]-a[i-1])-abs(a[i]-a[i+1])+abs(a[i-1]-a[i+1])\)

所以此时,\(ans_i=sum-abs(a[i]-a[i-1])-abs(a[i]-a[i+1])+abs(a[i-1]-a[i+1])\)

\(ans\) 取最小值即可

点击查看代码
#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,ans=0;
    cin>>n;

    vector<int> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(i!=1) ans+=abs(a[i]-a[i-1]);
    }

    int x=-inf;

    for(int i=1;i<=n;i++){
        int val=0;
        if(i==n) val=abs(a[i]-a[i-1]);
        else if(i==1) val=abs(a[i]-a[i+1]);
        else{
            val=abs(a[i]-a[i-1])+abs(a[i]-a[i+1])-abs(a[i-1]-a[i+1]);
        }
        x=max(x,val);
    }

    // sum-abs(a[i]-a[i-1])-abs(a[i]-a[i+1])+abs(a[i-1]-a[i+1])

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

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

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

}

C. Blackslex and Number Theory

先考虑对 \(a\) 数组排序,此时 \(a[1]\) 为最小值,\(a[2]\) 为次小值

考虑让 \(k=a[1]\) 是否成立?显然成立,因为此时 \(a[1]~mod~k=0\),而对于其他的数,也可以选择 \(x=a[i]\),使得 \(a[i]~mod~x=0\)

考虑让 \(k=a[2]\) 是否成立?

不成立,因为此时,\(a[1]~mod~a[2]=a[1]\),但 \(a[2]~mod~k=0\)\(a[2]~mod~x(x>k)=a[2]\)

显然不相等,所以不成立

再考虑 \(k\)\([a[1]+1,a[2]-1]\) 范围内,此时 \(a[1]~mod~k=a[1]\)

要使得 \(a[2]~mod~k=a[1]\),则 \(k\) 最大可以取 \(a[2]-a[1]\)

如果\(a[2]-a[1]>a[1]\)\(k\) 可以取到 \(a[2]-a[1]\),此时其他数,也可以找到一个 \(x=a[i]-a[i]>k\),使得 \(a[i]~mod~x=a[1]\)

否则 \(k\) 最大只能取 \(a[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);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    sort(a.begin()+1,a.end());
    int ans=a[1];

    if(a[2]-a[1]>a[1]) ans=a[2]-a[1];

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

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

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

}

D. Blackslex and Penguin Civilization

为了最大化 \(S(p)\),我们需要让前缀的 \(\text{AND}\) 值尽可能大,并且保持尽可能长的时间。

显然,排列的第一个数必须是 \(2^n-1\)(全 \(1\)),这样初始的 \(\text{popcount}\) 才是 \(n\)

维护一个当前的 \(\text{mask}\)(初始为 \(2^n-1\))。

当我们将 \(\text{mask}\) 的最高位去掉(即变为 \(\text{mask} >> 1\))之前,应该把所有满足 \((x \ \& \ \text{mask}) == \text{mask}\) 且未被使用的数 \(x\) 全部填入。

原因如下:

关于得分:对于满足条件的 \(x\),填入后前缀 \(\text{AND}\) 值依然是 \(\text{mask}\),不会变小,从而“白嫖”了当前的 \(\text{popcount}\) 分数。

关于字典序:我们在枚举满足条件的 \(x\) 时,从小到大枚举即可保证字典序最小。

关于 Mask 变换:为了让靠前的数字尽可能小,我们应当每次去掉 \(\text{mask}\) 的最高位(或者理解为右移),而不是去掉低位。

所以策略是:初始化 \(\text{mask} = 2^n-1\),先输出这个数。然后进行 \(n\) 轮循环,每次将 \(\text{mask}\) 更新为 \(\text{mask} >> 1\)。在每一轮中,从小到大遍历 \(0 \dots 2^n-1\),如果当前数 \(j\) 未被访问且 \((j \ \& \ \text{mask}) == \text{mask}\),则将其加入排列。

(最后一步可以不用遍历 \(0 \dots 2^n-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;

    int now=n;
    vector<bool> a(1<<n,false);

    while(now){
        int mask=(1<<now)-1;
        cout<<mask<<" ";
        a[mask]=1;
        int t=n-now;
        for(int i=0;i<(1<<t)-1;i++){
            int val=(i<<now)|mask;
            if(!a[val]){
                a[val]=1;
                cout<<val<<" ";
            }
        }
        now--;
    }

    for(int i=0;i<(1<<n);i++){
        if(!a[i]) cout<<i<<' ';
    }

    cout<<endl;

}
	
signed main(){

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

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

}

E. Blackslex and Girls

先构造出一种策略,将 \(p[i]\) 在符合要求的前提下填满,如果填不满或不符合要求,则可以直接输出 NO 了

记此时在区域 \(i\),有 \(a[i]\)\(x\)\(b[i]\)\(y\)

现在,每个位置都填到了 \(p[i]\),假设现在 \(x,y\) 都剩下一些数,则 \(x,y\) 之间可以一一抵消,此时较小数变为零。

假设此时 \(x<y\),所以 \(x=0,y!=0\)

分类讨论:

\(s\) 有 1 有 0,则可以将 \(y\) 分别放进 \(y\) 获胜的区域,结束

\(s\) 只有 1,同上

\(s\) 只有 0,此时,需要把 \(y\) 放到 \(x\) 赢的区域里,对于区域 \(i\),最多可以放: \(a[i]-b[i]-1\)\(y\),才能满足这个区域的 \(x\) 还是大于 \(y\)

\(x>y\) 时同理

可以证明,初始的 \(p[i]\) 只要保证符合胜负要求,\(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 n,x,y;
    cin>>n>>x>>y;

    string s;
    cin>>s;
    s=" "+s;

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

    vector<int> a(n+1),b(n+1);
    
    for(int i=1;i<=n;i++){
        if(s[i]=='0'){
            int cnt=p[i]/2+1;
            if(x>=cnt){
                a[i]=cnt;
                x-=cnt;
            }
            else{
                cout<<"NO\n";
                return;
            }   
        }
        else{
            int cnt=p[i]/2+1;
            if(y>=cnt){
                b[i]=cnt;
                y-=cnt;
            }
            else{
                cout<<"NO\n";
                return;
            }  
        }
    }

    for(int i=1;i<=n;i++){
        if(s[i]=='0'){
            //a[i]=p/2+1
            if(x>=p[i]-a[i]){
                x-=p[i]-a[i];
                a[i]=p[i];
            }
            else{
                a[i]+=x;
                x=0;
                if(y>=p[i]-a[i]){
                    y-=p[i]-a[i];
                    b[i]=p[i]-a[i];
                }
                else{
                    cout<<"NO\n";
                    return;
                }
            }
        }
        else{
            //b[i]=p/2+1
            if(y>=p[i]-b[i]){
                y-=p[i]-b[i];
                b[i]=p[i];
            }
            else{
                b[i]+=y;
                y=0;
                if(x>=p[i]-b[i]){
                    x-=p[i]-b[i];
                    a[i]=p[i]-b[i];
                }
                else{
                    cout<<"NO\n";
                    return;
                }
            }
        }
    }

    if(x || y){
        int t=min(x,y);
        x-=t,y-=t;
    }

    if(x){
        for(int i=1;i<=n;i++){
            if(s[i]=='0'){
                break;
            }
            else{
                // int diff=a[i]-b[i]-1;
                int diff=b[i]-a[i]-1;
                x-=diff;
                if(x<=0) break;
            }
            if(i==n){
                cout<<"NO\n";
                return;
            }
        }
    }
    if(y){
        for(int i=1;i<=n;i++){
            if(s[i]=='1'){
                break;
            }
            else{
                // int diff=b[i]-a[i]-1;
                int diff=a[i]-b[i]-1;
                y-=diff;
                if(y<=0) break;
            }
            if(i==n){
                cout<<"NO\n";
                return;
            }
        }
    }

    cout<<"YES\n";
}
	
signed main(){

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

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

}
posted @ 2025-12-24 01:15  LYET  阅读(20)  评论(0)    收藏  举报