2024.12.19 Codeforces Global Round 28

比赛连接


Solved: 6/10


A. Kevin and Combination Lock

题意:给一个整数,每次你可以将它减 33 或擦掉一个 33,问能否变成 0。

注意到擦掉 33 不会改变原数模 3 和 11 的余数,因此只需判断原数能否被 33 整除。

#include<bits/stdc++.h>
using namespace std;

void solve(){
    int n;
    cin>>n;
    cout<<(n%33?"NO":"YES")<<'\n';
}
int main(){
    int T;
    cin>>T;
    while(T--)solve();
}

B. Kevin and Permutation

题意:构造一个排列 \(p\),使得 \(\sum_{i=1}^{n-k+1}\min_{j=1}^{j+k-1}p_j\) 最小。

每隔 \(k\) 个位置放尽量小的数即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve(){
    int n,k;
    cin>>n>>k;
    vector<int> a(n+1);
    int j=1;
    for(int i=k;i<=n;i+=k)a[i]=j,++j;
    for(int i=1;i<=n;++i)if(!a[i])a[i]=j,++j;
    for(int i=1;i<=n;++i)cout<<a[i]<<' ';
    cout<<'\n';
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

C. Kevin and Binary Strings

题意:给一个 01 串,找出两个子串使得它们的异或和最大。\(|s|\leq 5000\)

因为位数越多越大,所以其中一个子串必为整个串。而另一个串需要高位尽可能是 1。原串的前几个 1 显然可以保留,找到从高位开始第一个 0 的位置,从这一位开始的长度就是第二个串的长度。因为 \(n^2\) 可过,所以直接枚举第二个串的起始位置即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve(){
    string a;
    cin>>a;
    int n=a.length(),pos=-1;
    for(int i=0;i<n;++i)a[i]-='0';
    for(int i=0;i<n;++i)if(!a[i]){pos=i;break;}
    if(pos==-1){cout<<1<<' '<<n<<' '<<1<<' '<<1<<'\n';return;}
    string mx="";
    int ans=0;
    for(int j=0;j<pos;++j){
        string s=a;
        for(int k=0;k<n-pos;++k)s[pos+k]^=a[j+k];
        if(s>mx)mx=s,ans=j;
    }
    cout<<1<<' '<<n<<' '<<ans+1<<' '<<ans+n-pos<<'\n';
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

D. Kevin and Competition Memories

题意:\(n\) 个人,第 \(i\) 个人水平为 \(a_i\)\(m\) 道题,第 \(j\) 道题难度为 \(b_j\)。每个人能做出且仅能做出 \(b_j\leq a_i\) 的题。
每场比赛 \(k\) 道题,举办 \(\lfloor mk\rfloor\) 场比赛,每道题只能供一场比赛。\(n,m\leq 3\times 10^5\)
对每个 \(k\),求所有比赛第 \(1\) 个人排名之和的最小值。

考虑每个人对答案的贡献。设满足 \(a_1<b_j\leq a_i\)\(j\) 共有 \(t_i\) 个,则

\[ans_k = \left\lfloor\frac mk\right\rfloor + \sum_{i=2}^n \left\lfloor\frac{t_i}k\right\rfloor + [t_i\bmod k > m\bmod k] = \left\lfloor\frac mk\right\rfloor + \sum_{i=2}^n \left\lfloor\frac mk\right\rfloor - \left\lfloor\frac{m-t_i}k\right\rfloor. \]

\(t_i\) 可以排序后双指针求,处理贡献时可用整除分块+差分数组优化为 \(O(\sqrt m)\)。总复杂度 \(O(n\sqrt m)\)

好像有点卡常(赛时提交 1900+ms)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define all(x) (x).begin(),(x).end()

void solve(){
    int n,m;
    cin>>n>>m;
    vector<int> a(n),b(m);
    for(int& x:a)cin>>x;
    for(int& x:b)cin>>x;
    int a0=a[0];
    sort(all(a)),sort(all(b));
    int cnt=0,p=n,q=m;
    for(int i=0;i<n;++i)if(a[i]>a0){p=i;break;}
    for(int i=0;i<m;++i)if(b[i]>a0){q=i;break;}
    vector<ll> s(m+2);
    for(int i=p,j=q;i<n;++i){
        while(j<m&&b[j]<=a[i])++j;
        int t=m-(j-q);
        for(int l=1,r;l<=t;l=r+1){
            r=t/(t/l);
            s[l]-=t/l,s[r+1]+=t/l;
        }
    }
    for(int i=1;i<=m;++i)s[i]+=s[i-1];
    for(int i=1;i<=m;++i)s[i]+=1ll*(n-p+1)*(m/i),cout<<s[i]<<' ';
    cout<<'\n';
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

E. Kevin and Bipartite Graph

题意:完全二分图 \(K(2n,m)\),给每条边染色为 \(n\) 种颜色,给出染色方案使得不存在同色环。

纯试出来的构造,不知道有没有更简洁的方法。

\(2n\leq m\) 无解,直接输出 NO。

\(n=4\) 为例:

1 1 2 3 4 2 3 4
2 2 3 4 1 3 4 1
3 3 4 1 2 4 1 2
4 4 1 2 3 1 2 3
1 2 3 4 1 2 3 4
2 3 4 1 2 3 4 1
3 4 1 2 3 4 1 2
4 1 2 3 4 1 2 3

取前 \(m\) 列。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define all(x) (x).begin(),(x).end()

void solve(){
    int n,m;
    cin>>n>>m;
    if(n*2<=m){
        cout<<"NO\n";
        return;
    }
    vector<vector<int>> a(2*n);
    for(int i=0;i<2*n;++i)a[i].resize(2*n);
    for(int i=0;i<n;++i){
        a[i][0]=a[i][1]=i;
        for(int j=0;j<n;++j)a[n+i][j]=a[n+i][n+j]=(i+j)%n;
        for(int j=1;j<n;++j)a[i][j+1]=a[i][j+n]=(i+j)%n;
    }
    cout<<"YES\n";
    for(int i=0;i<n*2;++i,cout<<'\n')
        for(int j=0;j<m;++j)
            cout<<a[i][j]+1<<' ';
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}

F. Kevin and Math Class

题意:给两个序列 \(a\)\(b\)。每次操作可以选择一个区间 \([l,r]\),令 \(x=\min_{j=l}^r b_j\),然后对每个 \(l\leq i\leq r\)\(a_i\leftarrow \lceil\frac{a_i}x\rceil\)。问最少几次操作可使序列 \(a\) 全为 \(1\)\(n\leq 2\times 10^5, a_i\leq 10^{18}\)

容易发现

  • 总操作数不超过 \(\log w\leq 60\)
  • 操作顺序一定是从小区间到大区间,除的数从大到小。

\(b\) 序列建立笛卡尔树,设 \(f(u,k)\) 表示以 \(u\) 为顶点的子树共操作 \(k\) 次最大值的最小值。则有转移

\[f(u,i+j+k)\leftarrow \left\lceil\frac{\max\{a_u,f(lc_u,i),f(rc_u,j)\}}{b_u^k}\right\rceil. \]

分两步转移,总复杂度 \(O(n\log^2 w)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define all(x) (x).begin(),(x).end()

const int N=2e5+5;
int n;
ll a[N],b[N];
int st[20][N];
int mini(int i,int j){
    return b[i]<=b[j]?i:j;
}
int qmni(int l,int r){
    int o=__lg(r-l+1);
    return mini(st[o][l],st[o][r-(1<<o)+1]);
}
ll f[N][60];
int rt,lc[N],rc[N];
int bld(int l,int r){
    int mid=qmni(l,r);
    lc[mid]=mid>l?bld(l,mid-1):0;
    rc[mid]=mid<r?bld(mid+1,r):0;
    return mid;
}
void dfs(int u){
    if(lc[u])dfs(lc[u]);
    if(rc[u])dfs(rc[u]);
    memset(f[u],0x3f,sizeof(ll)*60);
    for(int i=0;i<60;++i)
        for(int j=0;i+j<60;++j)
            f[u][i+j]=min(f[u][i+j],max(a[u],max(f[lc[u]][i],f[rc[u]][j])));
    ll g[60];
    memset(g,0x3f,sizeof(ll)*60);
    for(int i=0;i<60;++i){
        ll t=f[u][i];
        for(int j=0;i+j<60;++j)
            g[i+j]=min(g[i+j],t),t=(t+b[u]-1)/b[u];
    }
    memcpy(f[u],g,sizeof(ll)*60);
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=n;++i)cin>>b[i],st[0][i]=i;
    for(int i=1;1<<i<=n;++i)
        for(int j=1;j+(1<<i)-1<=n;++j)
            st[i][j]=mini(st[i-1][j],st[i-1][j+(1<<i-1)]);
    rt=bld(1,n);
    dfs(rt);
    for(int i=0;i<60;++i)if(f[rt][i]==1){cout<<i<<'\n';return;}
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
}
posted @ 2024-12-20 02:27  EssnSlaryt  阅读(228)  评论(0编辑  收藏  举报