CF 751 div2 题解

A

A 题只需要求出最小的字符,然后把其余的字符按顺序输出即可,没有什么难度。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N number
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int t;
string s;

int main(){
    read(t);
    while(t--){
        cin>>s;
        int len=s.length();
        int Minc=INF;
        for(int i=0;i<len;i++){
            Minc=min(Minc,(int)s[i]);
        }
        putchar((char)Minc);
        putchar(' ');
        for(int i=0;i<len;i++){
            if(s[i]==Minc) Minc=0;
            else putchar(s[i]);
        }
        puts("");
    }
    return 0;
}

B

容易发现这个序列的变化次数不会超过 (2000) 次,这个事实不难证明,或者说,这个东西看起来就是对的。所以这个题就相当于模拟。

模拟即可。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 2010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int cnt[N];
int a[N][N],n,t,q;

inline void Init(){
    read(n);
    for(int i=1;i<=n;i++) read(a[0][i]);
}

inline void Change(int id){
    for(int i=1;i<=n;i++) cnt[i]=0;
    for(int i=1;i<=n;i++) cnt[a[id-1][i]]++;
    for(int i=1;i<=n;i++) a[id][i]=cnt[a[id-1][i]];
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        Init();
        for(int i=1;i<=2000;i++) Change(i);
        read(q);
        for(int i=1;i<=q;i++){
            int x,k;read(x);read(k);
            if(k>2000) k=2000;
            printf("%d
",a[k][x]);
        }
    }
}

C

注意到答案就是所有非零的二进制位,把每一位出现次数加起来,然后直接做 (gcd) 就可以,注意判断全是 (0) 的情况。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int t,n,a[N],cnt[N],ans[N],tail;

inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}

inline void Init(){
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        tail=0;
        for(int i=0;i<=30;i++) cnt[i]=0;
        Init();
        bool op=1;
        for(int i=1;i<=n;i++) if(a[i]!=0) op=0;
        if(op){
            for(int i=1;i<=n;i++) printf("%d ",i);puts("");continue;
        }
        for(int i=1;i<=n;i++){
            for(int j=30;j>=0;j--){
                if((a[i]>>j)&1) cnt[j]++;
            }
        }
        int g=-1;
        for(int i=30;i>=0;i--){
            if(!cnt[i]) continue;
            if(g==-1) g=cnt[i];
            else g=gcd(g,cnt[i]);
        }
        int i;
        for(i=1;i*i<g;i++){
            if(g%i==0){
                ans[++tail]=i;ans[++tail]=g/i;
            }
        }
        if(g==i*i) ans[++tail]=i;
        sort(ans+1,ans+tail+1);
        for(int i=1;i<=tail;i++) printf("%d ",ans[i]);
        puts("");
    }
}

D

D 题一开始没有想到图论建模,然后在 dalao nyy 的秒杀之下才发现这个东西其实直接硬上线段树优化建图就行。

发现这个东西我们可以建两列点,左边一列向右边一列连边表示掉下去,右边向左边表示向上走,容易发现右边向左边连边其实连的是一段区间,可以用线段树优化建图,然后建完图直接跑最短路就可以。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 300010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

struct edge{
    int to,next,w;
    inline void Init(int to_,int ne_,int w_){
        to=to_;next=ne_;w=w_;
    }
}li[20*N];
int head[5*N],tail,tot,rt,rk[N*5];
int id[N],n,a[N],b[N],ID[N],at,flag;
int d[N*5],s,nowtot;
bool vis[N*5];
pair<int,int> Pre[N*5];
typedef pair<int,int> P;
pair<int,P> ans[N*5];

inline void Add(int from,int to,int w){
    li[++tail].Init(to,head[from],w);
    head[from]=tail;
}

#define ls(k) p[k].ls
#define rs(k) p[k].rs

struct Node{
    int ls,rs;
}p[N<<2];

inline int Build(int l,int r){
    if(l==r){
        ++tot;id[l]=tot;rk[tot]=l;
        if(l==0) flag=tot;
        return tot;
    }
    tot++;int k=tot;
    int mid=(l+r)>>1;
    ls(k)=Build(l,mid);rs(k)=Build(mid+1,r);
    Add(k,ls(k),0);Add(k,rs(k),0);
    return k;
}

inline void Find(int k,int now,int l,int r,int z,int y){
    if(l==z&&r==y){Add(now,k,1);return;}
    int mid=(l+r)>>1;
    if(y<=mid) Find(ls(k),now,l,mid,z,y);
    else if(z>mid) Find(rs(k),now,mid+1,r,z,y);
    else{Find(ls(k),now,l,mid,z,mid);Find(rs(k),now,mid+1,r,mid+1,y);}
}

inline void Init(){
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++) read(b[i]);
}

inline void Mapping(){
    nowtot=tot;
    for(int i=1;i<=n;i++){
        ID[i]=++tot;
    }
    // for(int i=1;i<=n;i++) printf("id[%d]=%d
",i,id[i]);
    for(int i=1;i<=n;i++) Add(id[i],ID[i+b[i]],0);
    for(int i=1;i<=n;i++){
        Find(rt,ID[i],0,n,i-a[i],i);
    }
}

deque<int> q;
inline void bfs(){
    memset(d,INF,sizeof(d));
    d[s]=0;
    q.push_back(s);
    while(q.size()){
        int top=q.front();q.pop_front();
        if(vis[top]) continue;
        vis[top]=1;
        for(int x=head[top];x;x=li[x].next){
            int to=li[x].to,w=li[x].w;
            if(d[to]<=d[top]+w) continue;
            d[to]=d[top]+w;Pre[to]=make_pair(top,w);
            if(w) q.push_back(to);
            else q.push_front(to);
        }
    }
}

inline void Print(){
    if(d[id[0]]==INF){puts("-1");return;}
    printf("%d
",d[id[0]]);
    int now=id[0];
    while(now){
        P pre=Pre[now];
        ans[++at]=make_pair(pre.first,make_pair(pre.second,now));
        now=pre.first;
    }
    reverse(ans+1,ans+at+1);
    // printf("flag=%d
",flag);printf("id[0]=%d
",id[0]);
    bool op=0;
    for(int i=1;i<=at;i++){
        if(ans[i].second.first) op=1;
        if(rk[ans[i].second.second]||ans[i].second.second==flag){
            op=0;printf("%d ",rk[ans[i].second.second]);
        }
    }
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    
    // printf("Complete Initing
");
    Init();rt=Build(0,n);Mapping();
    s=ID[n];bfs();Print();
}

E

这个题其实并不是很难,但是看了题解才恍然大悟。我们发现,(b) 一定是一个升序的数组,所以逆序对个数一定出自 (a,b) 两个序列之间以及 (a) 序列内部。(a) 序列内部的逆序对已经确定了,我们考虑两个序列之间的怎么算。

我们考虑 (b) 中的每个元素放进 (a) 中时是独立的,且位置是递增的,所以我们可以直接扫一遍,然后用树状数组维护逆序对,但这样实现难度较大。

我们考虑用分治,先解决中间,然后再做两边,同时维护值域,这样就可以做完了,复杂度 ((n+m)log n),最后用树状数组统计答案。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000100
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int posi[N],a[N],b[N],n,m,c[N<<1],tail,t,Pre[N],Suf[N];

struct BIT{
    int p[N<<1];
    inline int lowbit(int x){return x&(-x);}
    inline void PreAdd(int w,int x){
        for(int i=w;i<=tail;i+=lowbit(i)) p[i]+=x;
    }
    inline int PreAsk(int w){
        int res=0;for(int i=w;i>=1;i-=lowbit(i)) res+=p[i];return res;
    }
    inline void SufAdd(int w,int x){
        for(int i=w;i>=1;i-=lowbit(i)) p[i]+=x;
    }
    inline int AskSuf(int w){
        int res=0;for(int i=w;i<=tail;i+=lowbit(i)) res+=p[i];return res;
    }
    inline void Clear(int n){
        for(int i=1;i<=n;i++) p[i]=0;
    }
}bit;

inline void Init(){
    read(n);read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=m;i++) read(b[i]);
    for(int i=1;i<=n;i++) c[++tail]=a[i];
    for(int i=1;i<=m;i++) c[++tail]=b[i];
    sort(c+1,c+tail+1);int len=unique(c+1,c+tail+1)-c-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+len+1,a[i])-c;
    for(int i=1;i<=m;i++) b[i]=lower_bound(c+1,c+len+1,b[i])-c;
    sort(b+1,b+m+1);a[n+1]=INF;
}

inline void Solve(int l,int r,int Rl,int Rr){
    if(r<l) return;
    int mid=(l+r)>>1;
    Pre[Rl]=0;
    for(int i=Rl+1;i<=Rr;i++){
        Pre[i]=Pre[i-1];
        if(a[i-1]>b[mid]) Pre[i]++;
    }
    Suf[Rr]=0;
    for(int i=Rr-1;i>=Rl;i--){
        Suf[i]=Suf[i+1];
        if(b[mid]>a[i]) Suf[i]++;
    }
    int minn=INF,minp=-1;
    for(int i=Rl;i<=Rr;i++){
        if(minn>Pre[i]+Suf[i]){
            minn=Pre[i]+Suf[i];
            minp=i;
        }
    }
    posi[mid]=minp;
    Solve(l,mid-1,Rl,minp);
    Solve(mid+1,r,minp,Rr);
}

inline int GetAns(){
    // for(int i=1;i<=m;i++) printf("%d ",posi[i]);puts("");
    int now=0,ans=0;
    for(int i=1;i<=m;i++){
        while(now<posi[i]-1){
            now++;
            bit.SufAdd(a[now],1);
        }
        ans+=bit.AskSuf(b[i]+1);
    }
    // printf("ans=%d
",ans);
    bit.Clear(tail);
    now=n+1;
    for(int i=m;i>=1;i--){
        while(now>posi[i]){
            now--;
            bit.PreAdd(a[now],1);
        }
        ans+=bit.PreAsk(b[i]-1);
    }
    // printf("ans=%d
",ans);
    bit.Clear(tail);
    for(int i=n;i>=1;i--){
        ans+=bit.PreAsk(a[i]-1);
        bit.PreAdd(a[i],1);
    }
    // printf("ans=%d
",ans);
    bit.Clear(tail);
    tail=0;
    return ans;
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        Init();Solve(1,m,1,n+1);
        printf("%lld
",GetAns());
    }
    return 0;
}

F

这个题贪心非常妙,这个题其实我如果一个贪心策略与两个元素的大小都有关系,不妨考虑其最小值或最大值,或者加减乘除运算。这个题就是一个典型的考虑最大值的贪心。

先说贪心策略:按照 (max(s,a)) 排序,从小到大;然后再按照 (s) 从小到大排序,最后按照 (a)

我们考虑上面贪心策略的正确性:设 (max(s_i,a_i)<max(s_j,a_j))

  • (s_ige a_i,s_jge a_j)

    这种情况不难发现,如果我们先让 (i) 爬,(j) 一定可以爬,但是如果我们先让 (j) 爬,(i) 可能不能爬,所以我们先让 (i) 爬,不会使答案变劣。

  • (s_ige a_i,s_j<a_j)

    如果我们先让 (i) 爬,然后 (j) 一定可以爬,如果我们先让 (j) 爬,(i) 一定不能爬。我们先让 (i) 爬,会使答案变优。

  • (s_i< a_i,s_jge a_j)

    如果我们先让 (i) 爬,然后 (j) 一定可以爬,但是如果我们先让 (j) 爬,(i) 可能不能爬,所以我们先让 (i) 爬不会使答案变劣。

  • (s_i<a_i,s_j<a_j)

    如果我们先让 (i) 爬,(j) 可能能爬,如果 (j) 先爬,则 (i) 一定不能爬,让 (i) 先爬,不会是答案变劣。

同理,经过同样的分析,我们可以得到当最值相等时我们让 (s) 小的先爬不会使答案变劣。

排序直接跑就可以。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000100
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int posi[N],a[N],b[N],n,m,c[N<<1],tail,t,Pre[N],Suf[N];

struct BIT{
    int p[N<<1];
    inline int lowbit(int x){return x&(-x);}
    inline void PreAdd(int w,int x){
        for(int i=w;i<=tail;i+=lowbit(i)) p[i]+=x;
    }
    inline int PreAsk(int w){
        int res=0;for(int i=w;i>=1;i-=lowbit(i)) res+=p[i];return res;
    }
    inline void SufAdd(int w,int x){
        for(int i=w;i>=1;i-=lowbit(i)) p[i]+=x;
    }
    inline int AskSuf(int w){
        int res=0;for(int i=w;i<=tail;i+=lowbit(i)) res+=p[i];return res;
    }
    inline void Clear(int n){
        for(int i=1;i<=n;i++) p[i]=0;
    }
}bit;

inline void Init(){
    read(n);read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=m;i++) read(b[i]);
    for(int i=1;i<=n;i++) c[++tail]=a[i];
    for(int i=1;i<=m;i++) c[++tail]=b[i];
    sort(c+1,c+tail+1);int len=unique(c+1,c+tail+1)-c-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+len+1,a[i])-c;
    for(int i=1;i<=m;i++) b[i]=lower_bound(c+1,c+len+1,b[i])-c;
    sort(b+1,b+m+1);a[n+1]=INF;
}

inline void Solve(int l,int r,int Rl,int Rr){
    if(r<l) return;
    int mid=(l+r)>>1;
    Pre[Rl]=0;
    for(int i=Rl+1;i<=Rr;i++){
        Pre[i]=Pre[i-1];
        if(a[i-1]>b[mid]) Pre[i]++;
    }
    Suf[Rr]=0;
    for(int i=Rr-1;i>=Rl;i--){
        Suf[i]=Suf[i+1];
        if(b[mid]>a[i]) Suf[i]++;
    }
    int minn=INF,minp=-1;
    for(int i=Rl;i<=Rr;i++){
        if(minn>Pre[i]+Suf[i]){
            minn=Pre[i]+Suf[i];
            minp=i;
        }
    }
    posi[mid]=minp;
    Solve(l,mid-1,Rl,minp);
    Solve(mid+1,r,minp,Rr);
}

inline int GetAns(){
    // for(int i=1;i<=m;i++) printf("%d ",posi[i]);puts("");
    int now=0,ans=0;
    for(int i=1;i<=m;i++){
        while(now<posi[i]-1){
            now++;
            bit.SufAdd(a[now],1);
        }
        ans+=bit.AskSuf(b[i]+1);
    }
    // printf("ans=%d
",ans);
    bit.Clear(tail);
    now=n+1;
    for(int i=m;i>=1;i--){
        while(now>posi[i]){
            now--;
            bit.PreAdd(a[now],1);
        }
        ans+=bit.PreAsk(b[i]-1);
    }
    // printf("ans=%d
",ans);
    bit.Clear(tail);
    for(int i=n;i>=1;i--){
        ans+=bit.PreAsk(a[i]-1);
        bit.PreAdd(a[i],1);
    }
    // printf("ans=%d
",ans);
    bit.Clear(tail);
    tail=0;
    return ans;
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(t);
    while(t--){
        Init();Solve(1,m,1,n+1);
        printf("%lld
",GetAns());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/TianMeng-hyl/p/15481134.html