• gym102215题解


    A Rooms and Passages

    题意

    给n个数,从起点出发,一直往右走,遇到一个前面出现过其相反数的正数就停下,问对于每个起点都能走多少步。

    分析

    • 倒着递推,如果起点是正数,那么肯定可以走,ans[i]=ans[i+1]+1
    • 如果起点是负数,那就得看这个负数对应绝对值在后面出现的最靠前的位置,而且还要看后面的负数所对应的绝对值的最靠前位置,所以这个lst应该是全局更新的。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=5e5+50;
    int a[N],n,pos[N],ans[N];
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int lst=n+1;
        for(int i=n;i>=1;i--){
            if(a[i]>0){
                ans[i]=ans[i+1]+1;
                pos[a[i]]=i;
            }else{
                if(!pos[-a[i]]){
                    ans[i]=ans[i+1]+1;
                }else{
                    lst=min(lst,pos[-a[i]]-1);
                    ans[i]=lst-i+1;
                }
            }
        }
        for(int i=1;i<=n;i++){
            printf("%d%c",ans[i],i==n?'
    ':' ');
        }
        return 0;
    }
    

    B Rearrange Columns

    题意

    有两行字符串,有#.,可以对列进行重排,使得#连在一起。

    分析

    • 计算两个#,一个#在上面和在下面的,和没有#的,如果没有两个#而且#有上有下的,就不行。
    • 否则,把两个#的放中间即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+55;
    char a[2][N];
    int main(){
        scanf("%s",a[0]);
        scanf("%s",a[1]);
        int n=strlen(a[0]);
        int shang=0;
        int xia=0;
        int mei=0;
        int liang=0;
        for(int i=0;i<n;i++){
            if(a[0][i]=='#' && a[1][i]=='#'){
                liang++;
            }else if(a[0][i]=='#' && a[1][i]=='.'){
                shang++;
            }else if(a[0][i]=='.' && a[1][i]=='#'){
                xia++;
            }else{
                mei++;
            }
        }
        if(liang || (shang &&!xia) || (xia&&!shang)){
            printf("YES
    ");
            for(int i=0;i<mei;i++){
                printf(".");
            }
            for(int i=0;i<shang+liang;i++){
                printf("#");
            }
            for(int i=0;i<xia;i++){
                printf(".");
            }
            printf("
    ");
            for(int i=0;i<mei;i++){
                printf(".");
            }
            for(int i=0;i<shang;i++){
                printf(".");
            }
            for(int i=0;i<liang+xia;i++){
                printf("#");
            }
            printf("
    ");
        }else{
            printf("NO
    ");
        }
        return 0;
    }
    

    C Jumps on a Circle

    题意

    一个环标记为0到p-1,从0开始每次跳1,2,3...步,问跳n步之后会有多少个位置访问过。

    分析

    跳min(n,2p)次之后肯定会回到原点然后重新跳1,2,3...,所以模拟即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e7+50;
    ll n,p;
    int vis[N];
    int main(){
        scanf("%lld%lld",&p,&n);
        vis[0]=1;
        ll now=0;
        for(ll i=1;i<=min(n,2*p);i++){
            now=(now+i)%p;
            vis[now]=1;
        }
        int ans=0;
        for(ll i=0;i<p;i++){
            ans+=vis[i];
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    D Country Division

    题意

    给一棵树,每次询问给一些点染成红色,一些点染成蓝色,问能不能删掉一些边使得同色之间能相互到达,不同色之间不能相互到达。

    分析

    • 因为是一棵树,如果可以删的话肯定删一条边就够了。
    • 假设1为根,先求红色点的lca,再求蓝色点的lca,如果两个lca不在同一棵子树,那么删掉其中一个连向父节点的边即可。
    • 如果两个lca有祖先关系,比如红色lca和蓝色lca的lca就是红色lca,那么如果红色点都在红色lca指向蓝色lca的另一条边上,那就可以,否则,说明有红色点在蓝色点的子树下。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e5+50;
    struct Edge{
        int v,next;
    }e[N*2];
    int d[N],fa[N][20],pw[20];
    int cnt,head[N];
    void init(){
        pw[0]=1;
        for(int i=1;i<=20;i++){
            pw[i]=pw[i-1]*2;
        }
        cnt=0;
        memset(head,-1,sizeof(head));
    }
    void add(int u,int v){
        e[cnt]=Edge{v,head[u]};
        head[u]=cnt++;
        e[cnt]=Edge{u,head[v]};
        head[v]=cnt++;
    }
    void dfs(int u){
        for(int i=1;pw[i]<=d[u];i++){
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(v==fa[u][0]){
                continue;
            }
            fa[v][0]=u;
            d[v]=d[u]+1;
            dfs(v);
        }
    }
    int lca(int x,int y){
        if(d[x]<d[y]){
            swap(x,y);
        }
        int tmp=d[x]-d[y];
        for(int i=0;pw[i]<=tmp;i++){
            if(tmp&pw[i]){
                x=fa[x][i];
            }
        }
        if(x==y){
            return x;
        }
        for(int i=19;i>=0;i--){
            if(fa[x][i]!=fa[y][i]){
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    int n,q,u,v,ri,bi,x,red[N],blue[N];
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&n);
        init();
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        dfs(1);
        scanf("%d",&q);
        while(q--){
            scanf("%d%d",&ri,&bi);
            scanf("%d",&red[1]);
            int rlc=red[1];
            for(int i=2;i<=ri;i++){
                scanf("%d",&red[i]);
                rlc=lca(rlc,red[i]);
            }
            scanf("%d",&blue[1]);
            int blc=blue[1];
            for(int i=2;i<=bi;i++){
                scanf("%d",&blue[i]);
                blc=lca(blc,blue[i]);
            }
            int lc=lca(rlc,blc);
            if(rlc!=lc && blc!=lc){
                printf("YES
    ");
            }else if(rlc==lc){
                bool flag=false;
                for(int i=1;i<=ri;i++){
                    if(lca(red[i],blc)==blc){
                        printf("NO
    ");
                        flag=true;
                        break;
                    }
                }
                if(!flag){
                    printf("YES
    ");
                }
            }else if(blc==lc){
                bool flag=false;
                for(int i=1;i<=bi;i++){
                    if(lca(blue[i],rlc)==rlc){
                        printf("NO
    ");
                        flag=true;
                        break;
                    }
                }
                if(!flag){
                    printf("YES
    ");
                }
            }
        }
        return 0;
    }
    

    E Third-Party Software - 2

    题意

    求最少的线段覆盖整个区间。

    分析

    贪心经典题。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e5+50;
    struct node{
        int l,r,id;
        bool operator <(const node& rhs)const{
            if(l==rhs.l){
                return r>rhs.r;
            }else{
                return l<rhs.l;
            }
        }
    }a[N];
    int n,m;
    vector<int> res;
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].id=i;
        }
        sort(a+1,a+1+n);
        int now=1,i=1,ans=0;
        while(i<=n && now<=m){
            int mx=0;
            int k=0;
            while(i<=n && a[i].l<=now){
                if(a[i].r>mx){
                    mx=a[i].r;
                    k=i;
                }
                i++;
            }
            if(mx<=now-1){
                ans=-1;
                break;
            }
            res.push_back(a[k].id);
            now=mx+1;
            ans++;
        }
        if(now<=m){
            ans=-1;
        }
        if(ans==-1){
            printf("NO
    ");
        }else{
            printf("YES
    ");
            printf("%d
    ",ans);
            for(int i=0;i<ans;i++){
                printf("%d%c",res[i],i==ans-1?'
    ':' ');
            }
        }
        return 0;
    }
    

    F Friendly Fire

    题意

    n个怪兽,有两个属性a和b,如果属性a大于另一个怪兽的属性b,就可以打死该怪兽,现在要选出两个怪兽同时打,可能同时死,要使死的怪兽的属性a总和最大。

    分析

    • 参考了entry上一个外国朋友的思路https://codeforces.com/blog/entry/67178
    • 考虑分别对a和对b递增排序,得到两个序列,然后枚举第一个序列的怪兽,然后把第二个序列中当前怪兽能打败的怪兽的a值加入优先队列中,因为这些怪兽肯定能被打死,所以直接取出最大的a值,然后再看取出的怪兽能否打败枚举的怪兽,再考虑是否再更新答案。
    • 如果取出的怪兽是本身,就取下一个,注意空队列的判断,取出的怪兽如果没用到,continue之前也要重新入队

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=3e5+50;
    int n;
    struct node{
        int a,b,id;
    }a[N],b[N];
    bool cmp1(node a,node b){
        if(a.a==b.a){
            return a.b<b.b;
        }else{
            return a.a<b.a;
        }
    }
    bool cmp2(node a,node b){
        if(a.b==b.b){
            return a.a<b.a;
        }else{
            return a.b<b.b;
        }
    }
    priority_queue<pair<int,int> > q;
    int main(){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&a[i].a,&a[i].b);
            a[i].id=i;
            b[i]=a[i];
        }
        sort(a+1,a+1+n,cmp1);
        sort(b+1,b+1+n,cmp2);
        int ans=0;
        int le=0,ri=0;
        bool up=false;
        for(int i=1,j=1;i<=n;i++){
            while(j<=n && b[j].b<=a[i].a){
                q.push({b[j].a,j});
                j++;
            }
            if(q.empty()){
                continue;
            }
            int mx=q.top().first;
            int idx=q.top().second;
    //        printf("%d %d %d
    ",i,a[i].a,a[i].b);
    //        printf("%d %d %d %d
    ",idx,mx,b[idx].id,a[i].id);
            if(b[idx].id==a[i].id){
    //            printf("%d F
    ",i);
                auto tmp=q.top();
                q.pop();
                if(q.empty()){
                    q.push(tmp);
                    continue;
                }
                mx=q.top().first;
                idx=q.top().second;
                q.push(tmp);
            }
            int t=mx+(b[idx].a>=a[i].b?a[i].a:0);
            if(t>ans){
                up=true;
                ans=t;
                le=a[i].id;
                ri=b[idx].id;
            }
        }
        printf("%d
    ",ans);
        if(!up){
            printf("1 2
    ");
        }else{
            printf("%d %d
    ",le,ri);
        }
        return 0;
    }
    

    H Missing Number

    题意

    有0-n一共n+1个数,现在删去一个数,通过交互得到删去的数。

    交互是询问第x个数的第i位是0还是1。

    分析

    • 先分别询问每个数的最低位,统计1的个数,显然如果不缺数的话,1的个数应该是(frac{n+1}{2}),可以由此判断缺的数这一位是0还是1。

    • 然后可以排除掉一半不符合的数,继续询问第2位。

    • 用set存数字下标(即第几个数)比较好写。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+50;
    set<int> ns,t;
    int main(){
        int n;
        cin >> n;
        for(int i=1;i<=n;i++){
            ns.insert(i);
        }
        //从最低位开始询问
        int i=0;
        int res;
        int ans=0;
        while(ns.size()>=1){
            int sz=ns.size();
            t.clear();
            int b=0;
            for(auto it=ns.begin();it!=ns.end();it++){
                int x=*it;
                //询问第x个数的第i位
                cout << "? " << x << " " << i << endl;
                cout.flush();
                cin >> res;
                if(res){
                    t.insert(x);
                    b++;
                }
            }
            if(b<(sz+1)/2){
                //缺少的数第i位为1
                ans+=(1<<i);
                ns=t;
            }else{
                for(auto it=t.begin();it!=t.end();it++){
                    ns.erase(*it);
                }
            }
            i++;
        }
        cout << "! " << ans << endl;
        return 0;
    }
    

    I Painting a Square

    题意

    一个边长为b的小正方形给一个边长为a的大正方形涂色,问最小需要走的步数。

    分析

    • 沿着边缘螺旋走进去是最优的。
    • 所以先沿这边缘走两个边长,就转化为a-b的小正方形的情况了。
    • 本地跑不出来所以手动栈模拟,不过评测机上能跑出来。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll a,b;
    ll fun(ll a,ll b){
    //    printf("%lld %lld
    ",a,b);
        if(a-b<b){
            return 3ll*(a-b);
        }else{
            return fun(a-b,b)+2ll*(a-b)+b;
        }
    }
    stack<ll>st;
    ll solve(ll a,ll b){
        ll aa=a;
        while(aa-b>=b)
        {
            st.push(aa);
            aa-=b;
        }
        ll ans=3ll*(aa-b);
        while(!st.empty())
        {
            ans=ans+2ll*(st.top()-b)+b;
            st.pop();
        }
        return ans;
    }
    int main(){
        scanf("%lld%lld",&a,&b);
    //    ll ans=solve(a,b);
        ll ans=fun(a,b);
        printf("%lld
    ",ans);
        return 0;
    }
    

    J The Power of the Dark Side - 2

    题意

    n个人,每人有三种属性,如果有两种属性严格大于的话就能击败另一个人,问对于每个人,如果在每场战斗之前都能随意调整属性值,最多能击败多少人。

    分析

    • 考虑每个人能被击败的条件,显然就是对方属性值总和至少有(两个最小的属性值+2)。
    • 所以把每个人能被击败的下限求出来,排序,然后枚举每个人的属性总和,upper_bound()查出能击败的人数,再判断这些人里面是否有自身。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=5e5+55;
    ll a[N][3];
    ll sum[N];
    int n;
    vector<ll> v;
    vector<int> ans;
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&a[i][0],&a[i][1],&a[i][2]);
            sum[i]=a[i][0]+a[i][1]+a[i][2];
            sort(a[i],a[i]+3);
            v.push_back(a[i][0]+a[i][1]+2);
        }
        sort(v.begin(),v.end());
        for(int i=1;i<=n;i++){
            int k=upper_bound(v.begin(),v.end(),sum[i])-v.begin();
            ll t=sum[i]-(a[i][0]+a[i][1]+2);
            if(t>=0){
                k--;
            }
            ans.push_back(k);
        }
        for(int i=0;i<n;i++){
            printf("%d%c",ans[i],i==n-1?'
    ':' ');
        }
        return 0;
    }
    

    K Deck Sorting

    题意

    分析后的题意是给一个只含RGB的字符串,从中抽出一个子序列,和剩下的子序列拼在一起,使得拼接后的字符串相同字符在一起。

    分析

    • 枚举6个全排列,也就是拼接后的字符串情况,假设枚举RBG,那么就得把所有R取出。所有G剩下,要考虑的就是B,如果B是在最后一个R的后面,那就是跟随R取出,如果是在第一个G前面,那就跟随G留下,否则,即如果B在最后一个R前面且再第一个G后面,那就是不合法的。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+55;
    char s[N];
    set<char> lt;
    char idx[]={'R','G','B'};
    int main(){
        scanf("%s",s+1);
        int n=strlen(s+1);
        for(int i=1;i<=n;i++){
            lt.insert(s[i]);
        }
        int siz=lt.size();
        if(siz<3){
            printf("YES
    ");
        }else{
            for(int i=0;i<3;i++){
                char le=idx[i];
                for(int j=0;j<3;j++){
                    if(i==j){
                        continue;
                    }
                    char ri=idx[j];
                    char md=idx[3-i-j];
                    int leri=-1;
                    int rile=-1;
                    for(int k=n;k>=1;k--){
                        if(s[k]==le){
                            leri=k;
                            break;
                        }
                    }
                    for(int k=1;k<=n;k++){
                        if(s[k]==ri){
                            rile=k;
                            break;
                        }
                    }
                    bool flag=true;
                    for(int k=1;k<=n;k++){
                        if(s[k]==md){
                            if(k<leri && k>rile){
                                flag=false;
                                break;
                            }
                        }
                    }
                    if(flag){
                        printf("YES
    ");
                        return 0;
                    }
                }
            }
            printf("NO
    ");
        }
        return 0;
    }
    
    

    M Shlakoblock is live!

    题意

    n个东西有p属性和v属性,现在可以选择将一些东西的v属性加1,求最大的每个东西的p的期望。

    分析

    p越大,v加1对期望的贡献肯定越大,所以对p排序,然后对v加1,更新答案,直到对答案没有影响就退出。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1050;
    int t,n;
    struct node{
        ll zi,mu;
    };
    bool cmp(node a,node b){
        return a.zi*b.mu>a.mu*b.zi;
    }
    struct info{
        int p,v,id;
        bool operator <(const info& rhs)const{
            return p>rhs.p;
        }
    }a[N];
    vector<int> ans;
    int main(){
        // freopen("in.txt","r",stdin);
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            ll zi=0,mu=0;
            for(int i=1;i<=n;i++){
                scanf("%lld%lld",&a[i].p,&a[i].v);
                zi+=a[i].p*a[i].v;
                mu+=a[i].v;
                a[i].id=i;
            }
            sort(a+1,a+1+n);
            ans.clear();
            node tt{zi,mu};
            for(int i=1;i<=n;i++){
                zi+=a[i].p;
                mu++;
                node tmp={zi,mu};
                if(cmp(tmp,tt)){
                    ans.push_back(a[i].id);
                    tt=tmp;
                }else{
                    break;
                }
            }
            ll g=__gcd(tt.zi,tt.mu);
            printf("%lld/%lld
    ",tt.zi/g,tt.mu/g);
            int siz=ans.size();
            sort(ans.begin(),ans.end());
            printf("%d
    ",siz);
            if(!siz){
                printf("
    ");
            }
            for(int i=0;i<siz;i++){
                printf("%d%c",ans[i],i==siz-1?'
    ':' ');
            }
        }
        return 0;
    }
    
  • 相关阅读:
    NOIP2016 愤怒的小鸟
    LCIS code force 10D
    UVA 1398
    uva1382 Distant Galaxy
    洛谷-3930(我在洛谷上也写了题解)
    HDU-1505 City Game
    导弹拦截n logn的算法(单调性)洛谷1020
    POJ 1182 食物链
    POJ
    1202. 交换字符串中的元素
  • 原文地址:https://www.cnblogs.com/zxcoder/p/11699333.html
Copyright © 2020-2023  润新知