zoukankan      html  css  js  c++  java
  • 【清北学堂2018刷题冲刺】Contest 6

    Task 1:子集

    【问题描述】

     若一个集合S中任意两个元素x和y,都满足x⊕y<min⁡(x, y) ,则称集合S是“好的”。其中⊕为按位异或运算符。现在给定一个大小为n的集合S,其中每个数字都是正整数,请求出S所有“好的”子集中,元素个数最多的集合大小。

    【输入】

     输入文件含有多组数据

     每组数据第一行读入元素个数N。接下来一行,N个正整数ai描述集合中的元素。

    【输出】

     对于每组测试数据,输出一行一个整数表示答案.

    subset.in subset.out
    3 2
    1 2 3 2
    2
    1 1

    【样例解释】

     第一组数据中,选择集合{2,3}为最佳方案。若选择{1,2,3},由于1⊕2=3>min(1,2) ,该集合不是“好的”。

    【数据范围】

    • $1 – 4 $ \(1 ≤ N ≤ 16\)

    • $5 – 10 $ \(1 ≤ N ≤ 1000\)

    • 对于100%的数据:\(1 ≤ ai ≤ 10^9\)

     异或又名半加,可以理解为不进位加法。

     考虑三种形式:

    • 1^1=0
    • 0^0=0
    • 1^0=1

     只考虑最高位,若要使异或结果比两个数都小,最高位只能满足情况1。所以就可以直接按照二进制最高位数拆分,每个位数作为一个集合,求最大集合。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,arr[1010],bin[40],res[40];
    inline int read(){
        int s=0;
        char ch=getchar();
        while('9'<ch||ch<'0'){
            ch=getchar();
        }
        while('0'<=ch&&ch<='9'){
            s=(s<<3)+(s<<1)+ch-'0';
            ch=getchar(); 
        }
        return s;
    }
    int main(){
        freopen("subset.in","r",stdin);
        freopen("subset.out","w",stdout);
        bin[0]=1;
        for(register int i=1;i<=30;++i){
            bin[i]=bin[i-1]<<1;
        }
        while(scanf("%d",&n)==1){
            memset(res,0,sizeof(res));
            for(register int i=1;i<=n;++i){
                arr[i]=read();
                int pos=upper_bound(bin,bin+31,arr[i])-bin;
                res[pos]++;
            } 
            int ans=0;
            for(register int i=0;i<=30;++i){
                ans=max(ans,res[i]);
            }
            printf("%d\n",ans);
        }
        return 0;
    } 
    

    Task 2:出行

    【问题描述】

     小C要从A地赶往B地,A地和B地相距L米。在A去往B的路上,还有N个咖啡站,其中第i个咖啡站在距离A地xi 米的位置(0≤x1<x2<…<xN≤L )。小C平时步行的速度为a米/秒,而当他喝咖啡时,步行速度会是b米/秒(a<b )。

     每当小C到达一个咖啡站,小C可以选择购买一杯咖啡,购买咖啡所花费的时间可以忽略不计。刚买的咖啡由于太烫还不能立即饮用,所以小C会继续以a米/秒的速度前进。在买完咖啡t秒后,咖啡才变得可以饮用,此时小C便开始以b米/秒的速度前进。小C喝一杯咖啡需要r秒,即从开始喝咖啡r秒后,小C的前进速度又会变回a米/秒。当然小C到达咖啡站时,也可以选择不购买咖啡。

     另外,如果小C选择在一个咖啡站停下来购买咖啡,但此时手上还有一杯没有喝或者没喝完的咖啡,那么小C会毫不犹豫地扔掉手上之前购买的咖啡。

     请求出在最优决策下,小C从A地前往B地最少需要花费多少秒。

    【输入】

     第一行,五个整数L,a,b,t,r,分别表示两地间距离,小C不喝咖啡及喝咖啡时的速度,咖啡变凉的时长,以及饮用一杯咖啡需要的时间。

     第二行,一个整数N,描述A地到B地之间咖啡站的数量。

     接下来一行,N个单调上升的整数,表示第i个咖啡站与A地的距离。

    【输出】

     一行,表示答案,相对误差或绝对误差与标准答案相差不超过10^-6 即被认为正确。

    walk.in walk.out
    80 1 2 20 10 60
    3
    0 20 40

    【样例解释】

     最优决策下,小C会选择在第一个和第三个咖啡站买咖啡,第一次买咖啡时,小C以1米/秒的速度前进20秒,此时咖啡不再烫嘴,再以2米/秒的速度前进10秒,此时恰好到达第三个咖啡站。可以计算,最优花费为2*(10+20)=60 秒。

    【数据范围】

    • \(1 – 2\) \(0 ≤ N ≤ 20\)
    • \(3 – 6\) $ 0 ≤ N ≤ 1000$
    • \(7 – 10\) $ 0 ≤ N ≤ 5*10^5$
    • 对于100%的数据:保证\(1≤L≤10^{11} ,1≤a<b≤200 ,0≤t≤300 ,1≤r≤1200\)

     考虑路径为双色线段,前一半等待后一半加速。

     首先有一点可以确定:如果当前线段起点在某个线段加速点之前,从这个线段的转移一定不是最优。

     原因:前一个点选择而加速并未使用,直接转移一定不会最优。

     其他的情况分两类:

    • 当前的线段起点在某个线段加速点之后:部分加速
    • 当前线段起点在某个线段终点之后:完全加速,直接转移

     考虑直接转移:60pts

     因为我太弱了所以并没有摸索出来第一种情况怎么优化,只做了一个对第二种情况的单调队列加速。60pts->80pts

    #include<queue> 
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 500010
    #define lint long long
    using namespace std;
    
    lint n,l,sl,fa,wt,dr,head=1,tail=0,f[MAXN],que[MAXN];
    bool vis[MAXN];
    struct node{
        lint pos;
        lint fas;
        lint fin;
    }nod[MAXN];
    inline lint min(lint x,lint y){
        return x<y?x:y;
    }
    inline void update(lint k){
        while(head<=tail && que[tail]<=k){
            --tail;
        }que[++tail]=k;
    }
    int main(){
        freopen("walk.in","r",stdin);
        freopen("walk.out","w",stdout);
        scanf("%lld%lld%lld%lld%lld%lld",&l,&sl,&fa,&wt,&dr,&n);
        for(int i=1;i<=n;++i){
            scanf("%lld",&nod[i].pos);
            nod[i].fas=min(l,nod[i].pos+sl*wt);
            nod[i].fin=min(l,nod[i].fas+fa*dr);
        }
        nod[++n]=(node){l,l,l};
        for(register int i=1;i<=n;++i){
            f[i]=nod[i].fin-nod[i].fas;
            for(register int j=i;j>=1;--j){
                if(nod[i].pos<=nod[j].fas)continue;
                if(nod[i].pos>=nod[j].fin){
                    if(vis[j])break;
                    update(f[j]);
                    f[i]=max(f[i],nod[i].fin-nod[i].fas+que[head]);
                    vis[j]=true;
                }else{
                    f[i]=max(f[i],f[j]-(nod[j].fin-nod[i].pos)+nod[i].fin-nod[i].fas);
                }
            }
        }
        double ans=(double)f[n]/(double)fa+(double)(l-f[n])/(double)sl;
        printf("%lf\n",ans); 
    }
    

     调了一上午的100pts:不咕了在下面

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 1000010
    #define int long long
    using namespace std;
    struct node{
    	int pos;
    	double val;
    }que[MAXN];
    double f[MAXN];//f记录到达每一个咖啡站的花费时间 
    int l,n,a,b,t,r,x[MAXN];
    //vis用于记录对于一个能完整喝完咖啡的选择,以前是否访问过 
    signed main(){
    	freopen("walk7.in","r",stdin);
    //	freopen("walk.out","w",stdout);
    	memset(que,0,sizeof(que));
    	cin>>l>>a>>b>>t>>r>>n;
    	for(int i=1;i<=n;++i){
    		scanf("%lld",&x[i]);
    	}
    	double minn=1e50;
    	int head=1,tail=0,p1=1,p2=1,fst=0;
    	x[++n]=l;
    	for(int i=1;i<=n;++i){
    		//在这个点以前的选择都已经被处理过
    		while(x[i]-x[p1]>=t*a+r*b && p1<i){
    			//是一个可以完整喝完的选择 
    			minn=min(minn,(double)f[p1]+(double)r-( (double)x[p1]+(double)(r*b))/((double)a)); 
    			p1++;
    			fst=1;
    		}//更新可用最小值
    		if(t*a<=x[i]-x[p2] && x[i]-x[p2]<t*a+r*b && p2<i){
    			double ins=(double)f[p2]+(double)t-((double)(x[p2]+a*t))/(double)b;
    			while(head<=tail && ins<=que[tail].val)tail--;
    			que[++tail].val=ins;
    			que[tail].pos=x[p2];
    			p2++;
    		}
    		while(head<=tail && que[head].pos<=x[i]-t*a-r*b)head++;
    		f[i]=f[i-1]+((double)(x[i]-x[i-1]))/(double)a;
    		f[i]=min(f[i],minn+(double)x[i]/(double)a);
    		if(head<=tail){
    			f[i]=min(f[i],que[head].val+(double)x[i]/(double)b);
    		}
    	}
    	printf("%.8lf",f[n]);
    }
    
    

    Task 3:游戏

    【问题描述】

     有一个长度为n的排列,小C可以交换任意相邻元素任意多次,但第i 次交换需要付出i 的代价。小C认为逆序对是丑陋的,若在小C操作完的序列中共有x 个逆序对,那么小C就会认为这个序列的丑陋度是B*x ,其中B 是给定的常数。小C希望你告诉他最佳的操作策略,使得最终的序列丑陋度和操作所付出的代价之和最小。

    【输入】

     第一行,两个整数n ,B。

     接下来一行,n 个整数,描述初始排列。

    【输出】

     一行,表示代价与丑陋度的最小和。

    game.in game.out
    3 2 3
    3 1 2

    【样例解释】

     若不操作,代价+丑陋度=0+4=4

     若交换1和3,序列变成{1 3 2},代价+丑陋度=1+2=3

     若交换1和3,再交换2和3,序列变成{1 2 3},代价+丑陋度=3+0=3

     容易验证其他策略下,代价和丑陋度之和都不会小于3

    【数据范围】

    • \(1–2\) \(1 \leq N \leq 20\) \(1\leq N\leq20\)
    • \(3-6\) \(1 ≤ N \leq 5000\)
    • \(7 - 10\) \(1 ≤ N ≤ 5*10^4\)
    • 对于100%的数据:\(0 ≤ B ≤ 10^{15}\)

     逆序对水题,只要有逆序对就一定可以交换去掉,每次交换只能去掉一个。只需要求一下逆序对总数就可以了。B和n范围差距悬殊,为了避免高精度计算,可以先把换掉第n个逆序对的最大消耗和B比较一下,再确认是部分换还是全换。

     虽然水但有一点还是很重要的,就是要熟练归并和树状数组求逆序对的方法。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 50010
    #define lowbit(x) x&-x
    #define lint long long
    using namespace std;
    lint n,k,arr[MAXN],tree[MAXN];
    inline void add(int pos,int val){
        while(pos<=n){
            tree[pos]+=val;
            pos+=lowbit(pos);
        }
    }
    inline lint getsum(lint pos){
        lint res=0;
        while(pos){
            res+=tree[pos];
            pos-=lowbit(pos);
        }
        return res;
    } 
    inline lint get_rev(){
        lint res=0;
        for(register int i=n;i>=1;--i){
            res+=getsum(arr[i]);
            add(arr[i],1);
        }
        return res;
    }
    int main(){
        freopen("game.in","r",stdin);
        freopen("game.out","w",stdout);
        scanf("%lld%lld",&n,&k);
        for(register lint i=1;i<=n;++i){
            scanf("%lld",&arr[i]);
        }
        lint tot=get_rev(),ans=tot*k;
    //  printf("tot=%lld\n",tot);
        for(register lint i=1;i<=tot;++i){
            if(i>=k)break;
            ans+=i-k;
        }
        printf("%lld\n",ans);
        return 0;
    }
    
  • 相关阅读:
    《大话数据结构》最小生成树——Prim算法
    《大话数据结构》图的BFS和DFS
    寒假集训日志(三)——数论
    寒假集训日志(二)——最小生成树,拓扑排序,欧拉回路,连通路
    set
    寒假集训日志(一)——图,最短路问题
    经典的图论算法,C++描述
    动态数组
    stack and queue
    最长递增子序列,最长公共子串,最长公共子序列问题,最长公共增长子序列问题
  • 原文地址:https://www.cnblogs.com/maomao9173/p/9791429.html
Copyright © 2011-2022 走看看