zoukankan      html  css  js  c++  java
  • Codeforces Round #681 (Div. 2 含康托展开模板)

    A Kids Seating

    题意:有n个小朋友,有4 * n把编号1~4 * n的椅子,每个小朋友不能坐在编号gcd==1的位置

    思路:输出2 * n~4 * n之间的n个偶数

    B Saving the City

    题意:给一串01字符,1表示地雷,现在有两个费用,安置地雷a元(把0变成1),引爆地雷b元,地雷爆发会导致相邻的所有地雷爆炸,问最少输出多少钱

    思路:找两个1联通块中间的0的个数num,求num * b+a和2 * a的大小,小的话就是加上一个num * b-a,每个1的联通块都要+a

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define lson(x) x<<1
    #define rson(x) x<<1|1
    using namespace std;
    const int N=1e5+10;
    int a,b;
    char s[N];
    int zhi[N];
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d%d",&a,&b);
            scanf("%s",s);
            int l=strlen(s);
            int sum=0,num1=0,num0=0;
            int i=0;
            s[l]='0';
            for(i=0;i<l;i++){
                if(s[i]=='1'){
                    break;
                }
            }
            int cnt=0;
            for(;i<l;i++){
                if(s[i]=='1'&&s[i+1]=='0'){
                    zhi[cnt++]=num1+1;num1=0;
                }
                else if(s[i]=='0'&&s[i+1]=='1'){
                    zhi[cnt++]=num0+1;num0=0;
                }
                else if(s[i]=='1'&&s[i+1]=='1'){
                    num1++;
                }
                else{
                    num0++;
                }
            }
            for(int i=0;i<cnt;i++){
                if(i&1 && i!=cnt-1){
                    if(zhi[i]*b+a<2*a){
                        sum+=(zhi[i]*b-a);
                    }
                }
                else{
                    sum+=a;
                }
            }
            printf("%d
    ",sum);
        }
        return 0;
    }
    

    C The Delivery Dilemma

    题意:你有n个菜,可以选择叫外卖去拿外卖送家里和去店拿外卖。外卖去拿的外卖算最大的,自己去店里拿需要求和,问你拿完这个n个菜,最少时间是多少

    思路:按照叫外卖从小到大排序,自己去拿前缀和,然后o(n)方法比较即可。

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define lson(x) x<<1
    #define rson(x) x<<1|1
    using namespace std;
    const int N=2e5+10;
    int n;
    struct node{
        int a,b;
        friend bool operator<(const node a,const node b){
            if(a.a==b.a){
                return a.b<b.b;
            }
            return a.a<b.a;
        }
    }a[N];
    ll ans[N];
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i].a);
            }
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i].b);
            }
            sort(a+1,a+n+1);
            ans[0]=0;
            for(int i=1;i<=n;i++){
                ans[i]=a[i].b+ans[i-1];
            }
            ll mix=1e15;
            for(int i=1;i<=n;i++){
                mix=min(mix,max((ll)a[i].a,ans[n]-ans[i]));
            }
            mix=min(mix,ans[n]);//特判全部自己拿的情况
            printf("%lld
    ",mix);
        }
        return 0;
    }
    

    D Extreme Subtraction

    题意:给你一个n长度的数组,问有两种操作,选择一个k,1 ~ k下标数组值-1或者k ~ n下标数组值,问能不能使得数组全部为0,能输出yes,不能no

    思路:从前算后面能否形成递减序列,或者从后算前面能否形成递增序列

    给wa的盆友两个样例

    2
    6
    5 3 5 2 4 3
    6
    5 3 5 3 4 3
    
    yes
    no
    
    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define lson(x) x<<1
    #define rson(x) x<<1|1
    using namespace std;
    const int N=3e4+10;
    int n;
    int a[N],b[N],c[N];
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            int f=1;
            int ans=0,minl=1e9;
    	for(int i=n;i>=1;i--){
    	      a[i]-=ans;
    	      if(a[i]<0) f=0,break;
    	      if(a[i]<=minl) minl=a[i];
    	      else ans+=(a[i]-minl);
    	}
            if(f)
                printf("YES
    ");
            else
                printf("NO
    ");
        }
        return 0;
    }
    
    

    E Long Permutation

    题意:给你n长度的序列,a[i]=i。有m个操作,操作一求 l 到 r 数组和,操作二,将当前序列变成字典序第x小的序列。

    思路:因为x的范围是1e5 所以最多10个数字的全排列,所以只要动数组最后的10位,即可。这里需要康托展开的知识-^-

    因为是从第一字典序列开始累加变序列,k的初始值为1。因为我的k初始值0,所以逆康托没用k--这步操作

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define lson(x) x<<1
    #define rson(x) x<<1|1
    using namespace std;
    const int N=2e5+10;
    int a[25],vis[25];
    ll sum[N],qzh[25];
    void invKT(int ans[],int n,ll k){
        if(k==0){return;}
        for (int i=0;i<n;i++){
            ll t=k/qzh[n-i-1];int j;
            for (j=1;j<=n;j++)
                if(!vis[j]){
                    if (t==0) break;
                    --t;
                }
            ans[i]=j,vis[j]=true;
            k%=qzh[n-i-1];//余数
        }
        return;
    }
    int n,m;
    int main(){
        int t;
        qzh[0]=1;
        qzh[1]=1;sum[0]=0;
        for(int i=2;i<=17;i++){
            qzh[i]=i*qzh[i-1];//cout<<qzh[i]<<endl;
        }
        for(int i=1;i<N;i++){
            sum[i]=sum[i-1]+(ll)i;
        }
        int R;
        ll kk=0;
        scanf("%d%d",&n,&m);
        R=max(n-15,1);
        for(int i=0;i<m;i++){
            int op;scanf("%d",&op);
            if(op==1){
                int l,r;
                scanf("%d%d",&l,&r);
                if(R>r)
                    printf("%lld
    ",sum[r]-sum[l-1]);
                else{
                    for(int i=0;i<25;i++){vis[i]=0;a[i]=0;}
                    ll ans=0;
    		int start;
    		if(l<R){
    			ans+=sum[R-1]-sum[l-1];
    			start=R;
    		}
    		else{
    			start=l;
    		}
    		//cout<<(n-R+1)<<endl;
    		invKT(a,(n-R+1),kk);
    		for(int i=start-R;i<=r-R;i++){
    			if(a[i]==0) ans=ans+i+R;
                            else ans=ans+a[i]+(R-1);
    		}
    		printf("%lld
    ",ans);
                }
            }
            else{
                ll k;
                scanf("%lld",&k);kk+=k;
            }
        }
        return 0;
    }
    

    康托展开 + 逆康托 模板

    参考大佬博客全排列的编码与解码:康托展开 By Lucio.Yang

    1、康托展开:全排列到一个自然数的双射

    X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
    

    ai为整数,并且0<=ai<i(1<=i<=n)

    2、逆康托

    如何找出第16个(按字典序的){1,2,3,4,5}的全排列?

        1. 首先用16-1,得到15

        2. 用15去除4!,得到0余15

        3. 用15去除3!,得到2余3

        4. 用3去除2!,得到1余1

        5. 用1去除1!,得到1余0

    有0个数比它小的数是1,所以第一位是1,有2个数比它小的数是3,但1已经在之前出现过了所以是4,有1个数比它小的数是2,但1已经在之前出现过了所以是3,有1个数比它小的数是2,但1,3,4都出现过了所以是5,最后一个数只能是2,所以排列为1 4 3 5 2。

    代码模板

    void inint(){
        qzh[0]=1;
        qzh[1]=1;
        for(int i=2;i<=17;i++){
            qzh[i]=i*qzh[i-1];
        }
    }
    
    void invKT(int ans[],int n,ll k){
        k--;mem(vis,0);
        for (int i=0;i<n;i++){
            ll t=k/qzh[n-i-1];int j;
            for (j=1;j<=n;j++)
                if(!vis[j]){
                    if (t==0) break;
                    --t;
                }
            ans[i]=j,vis[j]=true;
            k%=qzh[n-i-1];//余数
        }
        return;
    }
    

    F Identify the Operations

    题意:给你n,m长度的两个数组(分为a,b),某人得到了长度为n的数组(1~n无序排列的数组),有m个操作,删除一个n长度中的一个数字,把这个数字的前一个或者后一个(第一位和最后一位只能选一个),按照操作顺序放进另一个数组里,问有多少种情况能的到m长度的数组,答案取模

    思路:标记a组数字在数组的位置,用pos数组存在b组每个数在a组的位置,并存进map里。for一遍,如果这个点的前、后下标在map里出现的则答案为0,如果这个点只出现过前或者后 * 1,都没出现过则 * 2,特判一下第一个和最后一个。然后把这个点从map中删除

    #include<bits/stdc++.h>
    using namespace std;
    #define mod 998244353
    #define ll long long
    const int N=2e5+10;
    int a[N],b[N],pos[N],cnt[N];
    int n,m;
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            map<int,int>mp;
            for(int i=0;i<n;i++)scanf("%d",&a[i]),pos[a[i]]=i;
            for(int i=0;i<m;i++)scanf("%d",&b[i]),cnt[i]=pos[b[i]],mp[pos[b[i]]]++;
            ll ans=1;
            for(int i=0;i<m;i++){
                if(cnt[i]==0){
                    if(mp[1]){ans=0;}
                    mp[0]--;
                }
                else if(cnt[i]==n-1){
                    if(mp[n-2]){ans=0;}
                    mp[n-1]--;
                }
                else{
                    ll k=2;
                    if(mp[cnt[i]-1])k--;
                    if(mp[cnt[i]+1])k--;
                    ans*=k;ans%=mod;
                    mp[cnt[i]]--;
                }
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    老年人了,要早睡早起(偷懒),于是开了模拟去做,做完这套感觉就是,div2就这???感觉在写div3一样 算了模拟赛卡了D,赛后看了F,想不出好方法,模拟了一遍居然AC了!

    多努力才能少打铁,锻炼自己的心态也很重要。希望别留遗憾吧

    这场的地址

  • 相关阅读:
    std::string构造函数
    shell命令-while语句
    shell命令-for语句
    shell命令-if语句
    softmax回归推导
    sigmod函数求导
    生成器面试题之一
    range和xrange的区别
    python里的input
    python中print和input的底层实现
  • 原文地址:https://www.cnblogs.com/luoyugongxi/p/13922519.html
Copyright © 2011-2022 走看看