cf1858 做题记录

发布时间 2023-08-24 22:14:04作者: luo_shen

A

题面

两个人肯定会先把 \(c\) 个轮流拿掉,最后比较谁走的回合多。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
void solve(){
    int a,b,c;
    read(a),read(b),read(c);
    a+=c/2+c%2;
    b+=c/2;
    if(a>b){
        puts("First");
    }
    else{
        puts("Second");
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t;
    read(t);
    while(t--){
        solve();
    }
    return 0;
}

B

题面

\(c\) 难的 \(b\)

因为删去一个关键点,影响的区间只有前一个关键点到后一个关键点的区间,不被影响的有一段前缀和一段后缀,可以预处理出每个前缀和后缀的贡献,枚举删掉哪个关键点并计算总贡献,注意特殊处理 \(1\)\(m\) 即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e5+10,inf=1e18;
int n,m,d,pos[N],nxt[N],pre[N];
void solve(){
    read(n),read(m),read(d);
    for(int i=1;i<=m;i++){
        read(pos[i]);
    }
    nxt[m]=(n-pos[m])/d+1;
    for(int i=m-1;i;i--){
        nxt[i]=nxt[i+1]+(pos[i+1]-pos[i]-1)/d+1;
    }
    if(pos[1]==1){
        pre[1]=1;
    }
    else{
        pre[1]=2+(pos[1]-2)/d;
    }
    for(int i=2;i<=m;i++){
        pre[i]=pre[i-1]+(pos[i]-pos[i-1]-1)/d+1;
    }
    int ans=inf,num=0;
    for(int i=1,sum;i<=m;i++){
        if(i==1){
            sum=nxt[2]+(pos[2]-2)/d+1;
        }
        else if(i<m){
            sum=(pos[i+1]-pos[i-1]-1)/d+pre[i-1]+nxt[i+1];
        }
        else{
            sum=pre[m-1]+(n-pos[m-1])/d;
        }
        if(ans>sum){
            ans=sum;
            num=1;
        }
        else if(ans==sum){
            num++;
        }
    }
    write_space(ans),write_endl(num);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t;
    read(t);
    while(t--){
        solve();
    }
    return 0;
}

C

题面

类似于夕阳西下几时回,选完一个数 \(x\) 之后,如果 \(2x\) 没被选,那么选择 \(2x\),此时 \(x\) 会作为 \(gcd\) 被选择,且以该策略操作后,所有小于等于 \(n/2\) 的数都会被选,显然最大。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
int n;
void solve(){
    read(n);
    set<int>s;
    for(int i=1;i<=n;i++){
        s.insert(i);
    }
    while(s.size()){
        int x=*(s.begin());
        while(x<=n){
            write_space(x);
            s.erase(x);
            x*=2;
        }
    }
    putchar('\n');
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t;
    read(t);
    while(t--){
        solve();
    }
    return 0;
}

D

题面

枚举区间 \([l,r]\) 为最长的连续 \(1\) 的区间,记其修改了 \(x\) 次,那么我们要在 \([1,l-1]\cup [r+1,n]\) 中找到修改后最长的连续 \(0\) 区间 \([l',r']\),记其修改了 \(y\) 次,且 \(x+y\le k\)
\(pre_{i,j}\) 表示前缀 \([1,i]\) 中,修改次数最多为 \(j\) 的连续 \(0\) 区间的长度的最大值,\(bac_{i,j}\) 表示后缀 \([i,n]\) 中,修改次数最多为 \(j\) 的连续 \(0\) 区间的长度的最大值。\(s_i\) 表示前缀 \([1,i]\) 中的 \(1\) 的数量,枚举区间 \([i,j]\)\(pre_{i,sum_j-sum_i}=i-j+1\),做一下前缀后缀 \(\max\) 即可处理得到。随后便可以得到连续 \(1\) 区间的长度为 \(x\) 时,连续 \(0\) 区间的长度的最大值 \(mx_x\),答案可以枚举得到。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=3010,inf=1e9;
int n,m,sum[N],pre[N][N],bac[N][N],mx[N];
char s[N];
void solve(){
    read(n),read(m);
    for(int i=1;i<=n;i++){
        s[i]=getchar();
        while(s[i]!='0'&&s[i]!='1'){
            s[i]=getchar();
        }
        sum[i]=sum[i-1]+s[i]-'0';
    }    
    for(int i=0;i<=n+1;i++){
        for(int j=0;j<=n;j++){
            pre[i][j]=bac[i][j]=0;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=i-1;~j;j--){
            pre[i][sum[i]-sum[j]]=i-j;
        }
        for(int j=0;j<=n;j++){
            pre[i][j]=max(pre[i][j],pre[i-1][j]);
        }
        for(int j=1;j<=n;j++){
            pre[i][j]=max(pre[i][j],pre[i][j-1]);
        }
    }
    for(int i=n;i;i--){
        for(int j=i;j<=n;j++){
            bac[i][sum[j]-sum[i-1]]=j-(i-1);
        }
        for(int j=0;j<=n;j++){
            bac[i][j]=max(bac[i][j],bac[i+1][j]);
        }
        for(int j=1;j<=n;j++){
            bac[i][j]=max(bac[i][j],bac[i][j-1]);
        }
    }
    for(int i=0;i<=n;i++){
        mx[i]=-inf;
    }
    mx[0]=pre[n][m];
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            int cnt=(j-i+1)-(sum[j]-sum[i-1]);
            if(cnt>m){
                continue;
            }
            mx[j-i+1]=max(mx[j-i+1],max(pre[i-1][m-cnt],bac[j+1][m-cnt]));
        }
    }
    for(int i=1;i<=n;i++){
        int ans=0;
        for(int j=0;j<=n;j++){
            ans=max(ans,i*mx[j]+j);
        }
        write_space(ans);
    }
    putchar('\n');
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t;
    read(t);
    while(t--){
        solve();
    }
    return 0;
}

E1

题面

离线特有做法,建操作树。

假设当前在 \(p\) 位置,一次加操作,相当于该点加一个儿子,权值为 \(x\),并移动到儿子的位置。一次减操作相当于跳到 \(p\)\(x\) 级祖先,对于撤销操作,可以记录每一次操作后的位置,撤销相当于回到上一次的位置。所有询问全部离线,最后一遍dfs找答案即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e6+10,Lg=20;
int n,f[N][Lg+5],stk[N],top,idx,val[N],buc[N],ans[N],cnt;
struct node{
    int opt,val;
}p[N];
vector<int>e[N];
void query(int u){
    if(u!=0){
        if(!buc[val[u]]){
            cnt++;
        }
        ++buc[val[u]];
    }
    ans[u]=cnt;
    for(auto v:e[u]){
        query(v);
    }
    if(u!=0){
        --buc[val[u]];
        if(!buc[val[u]]){
            cnt--;
        }
    }
}
void solve(){
    read(n);
    for(int i=1;i<=n;i++){
        char opt=getchar();
        while(opt!='+'&&opt!='-'&&opt!='!'&&opt!='?'){
            opt=getchar();
        }
        if(opt=='+'){
            p[i].opt=1;
            read(p[i].val);
        }
        else if(opt=='-'){
            p[i].opt=2;
            read(p[i].val);
        }
        else if(opt=='!'){
            p[i].opt=3;
        }
        else{
            p[i].opt=4;
        }
    }
    int pos=0;
    stk[++top]=pos;
    for(int i=1;i<=n;i++){
        if(p[i].opt==1){
            idx++;
            e[pos].pb(idx);
            val[idx]=p[i].val;
            f[idx][0]=pos;
            for(int i=1;i<=Lg;i++){
                f[idx][i]=f[f[idx][i-1]][i-1];
            }
            pos=idx;
            stk[++top]=pos;
        }
        else if(p[i].opt==2){
            for(int j=Lg;j>=0;j--){
                if(p[i].val>>j&1){
                    pos=f[pos][j];
                }
            }
            stk[++top]=pos;
        }
        else if(p[i].opt==3){
            pos=stk[--top];
        }
        else{
            p[i].val=pos;
        }
    }
    query(0);
    for(int  i=1;i<=n;i++){
        if(p[i].opt==4){
            write_endl(ans[p[i].val]);
        }
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

E2

题面

不得不说,这题有了e1之后难度直线飙升,都往优化操作树上去了。

假定没有撤回操作,考虑维护这个序列。用一个set和树状数组维护每个数第一次出现的位置。令 \(len\) 表示当前序列的长度,答案为 \(1\sim len\) 的区间中第一次出现的数的数量。带个撤回操作就记一下操作前的状态即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e6+10,mx=1e6;
int q,len,a[N],c[N];
set<int>s[N];
namespace Fenwick_Tree{
    int c[N];
    int lowbit(int x){
        return x&(-x);
    }
    void add(int pos,int val){
        while(pos<=mx){
            c[pos]+=val;
            pos+=lowbit(pos);
        }
    }
    int query(int pos){
        int ans=0;
        while(pos){
            ans+=c[pos];
            pos-=lowbit(pos);
        }
        return ans;
    }
}
struct node{
    int opt,x;
    node (int _opt=0,int _x=0){
        opt=_opt,x=_x;
    }
};
vector<node>stk;
void solve(){
    memset(a,-1,sizeof(a));
    read(q);
    while(q--){
        char opt=getchar();
        int x;
        while(opt!='+'&&opt!='-'&&opt!='?'&&opt!='!'){
            opt=getchar();
        }
        if(opt=='+'){
            read(x);
            len++;
            if(a[len]!=-1){
                if(s[a[len]].size()){
                    Fenwick_Tree::add(*s[a[len]].begin(),-1);
                    s[a[len]].erase(len);
                }
                if(s[a[len]].size()){
                    Fenwick_Tree::add(*s[a[len]].begin(),1);
                }
            }
            stk.pb(node(1,a[len]));
            a[len]=x;
            if(s[x].size()){
                Fenwick_Tree::add(*s[x].begin(),-1);
            }
            s[x].insert(len);
            Fenwick_Tree::add(*s[x].begin(),1);
        }
        else if(opt=='-'){
            read(x);
            len-=x;
            stk.pb(node(0,x));
        }
        else if(opt=='?'){
            write_endl(Fenwick_Tree::query(len));
            fflush(stdout);
        }
        else{
            node lst=stk.back();
            stk.pop_back();
            if(lst.opt){
                if(a[len]!=-1){
                    if(s[a[len]].size()){
                        Fenwick_Tree::add(*s[a[len]].begin(),-1);
                        s[a[len]].erase(len);
                    }
                    if(s[a[len]].size()){
                        Fenwick_Tree::add(*s[a[len]].begin(),1);
                    }
                }
                x=lst.x;
                a[len]=x;
                if(x!=-1){
                    if(s[x].size()){
                        Fenwick_Tree::add(*s[x].begin(),-1);
                    }
                    s[x].insert(len);
                    Fenwick_Tree::add(*s[x].begin(),1);
                }
                len--;
            }
            else{
                len+=lst.x;
            }
        }
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}