zoukankan      html  css  js  c++  java
  • 9.13——TEST NOIP模拟测试

    T1:

    题目描述:

    NYG有一个神奇的背包,每放进去一个物品,背包的体积就会变大。
    也就是说,每放进一个物品,背包会被占用一定的体积,但是紧接着背包的总体积又
    会增大一定的值(注意是在放入物品后背包总体积才增大)。
    NYG发觉这个背包十分好用,于是不由自主地想到了一个问题。
    现在给出背包初始容量V以及n个物品,每一个物品两个值a; b,分别表示物品所占体积
    和放入背包后背包增大的体积。
    NYG想知道能否把所有物品装进去?
    因为NYG比较老实,这么简单的问题自然装作不会做的样子。
    于是他来请教你。

    样例输入:

    3
    7 9269
    21366 1233
    7178 23155
    16679 23729
    15062 28427
    939 6782
    24224 9306
    22778 13606
    5 22367
    17444 5442
    16452 30236
    14893 24220
    31511 13634
    4380 29422
    7 18700
    25935 4589
    24962 9571
    26897 14892
    20822 2380
    21103 12648
    32006 22912
    23367 20674

    样例输出:

    Yes

    Yes

    No

    解析:

    考虑贪心。对于b>=a,显然对a权值从小到大排序即可。对于b<a,按b从大到小排序。下面给出证明:

    将整个过程倒着看,用了a体积相当于增加了a体积,增加了b体积相当于用了b体积。类比于b>=a的过程,我们肯定想让占背包体积小即b值小的优先。但由于是倒着看,所以正着的时候就按b从大到小排序。

    时间复杂度O(nlogn)

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    typedef long long LL;
    const int MAXN=100010;
    int T;
    struct Backpack{
        int a,b;
    }s1[MAXN],s2[MAXN];
    int n;
    LL h;
    int cnt1=0,cnt2=0;
    
    inline int read(){
        int ret=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-') f=-f;c=getchar();}
        while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
        return ret*f;
    }
    
    bool cmp1(Backpack A,Backpack B){
        return A.a<B.a;
    }
    
    bool cmp2(Backpack A,Backpack B){
        return A.b>B.b;
    }
    
    int main(){
        freopen("backpack.in","r",stdin);
        freopen("backpack.out","w",stdout);
        T=read();
        while(T--){
            cnt1=0;cnt2=0;
            n=read();h=1LL*read();
            for(int i=1;i<=n;++i){
                    int a=read(),b=read();
                    if(b-a>=0){
                        s1[++cnt1].a=a;s1[cnt1].b=b;
                    }else{
                        s2[++cnt2].a=a;s2[cnt2].b=b;
                    }
            }
            sort(s1+1,s1+cnt1+1,cmp1);
            bool flag=false;
            for(int i=1;i<=cnt1;++i){
                h-=1LL*s1[i].a;
                if(h<0){
                    printf("No
    ");
                    flag=true;
                    break;
                }
                h+=1LL*s1[i].b;
            }
            if(flag) continue;
            sort(s2+1,s2+cnt2+1,cmp2);
            for(int i=1;i<=cnt2;++i){
                h-=1LL*s2[i].a;
                if(h<0){
                    printf("No
    ");
                    flag=true;
                    break;
                }
                h+=1LL*s2[i].b;
            }
            if(flag) continue;
            printf("Yes
    ");
        }
        return 0;
    }
    View Code

    T2:

    题目描述:

    NYG是一个善于思考的好学生。
    于是NYG想往他的背包中放序列。
    某一天NYG得到了一个长度为n的序列{ai}。然后NYG说,如果对于一段区间[L, R],
    存在L ≤ k ≤ R使得∀i ∈ [L, R]都有ak|ai,我们认为它有价值,价值为R - L(若不满足条
    件则没有价值)。
    现在NYG想知道所有区间中价值大为多少,最大价值的区间有多少个,以及这些区
    间分别是什么。

    样例输入:

    30
    15 15 3 30 9 30 27 11 5 15 20 10 25 20 30 15 30 15 25 5 10 20 7 7 16 2 7 7 28 7

    样例输出:

    1 13

    9

    解析:

    看到题目中问满足条件的最大值,便想到二分答案。问题在于怎么check。我们发现,对于一个区间[l,r],满足条件的ak值一定是这段区间的gcd,并且是这段区间的最小值。用ST表来维护即可。

    预处理复杂度O(nlogn²),check复杂度O(nlogn),总时间复杂度O(nlogn²)。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    typedef long long LL;
    const int MAXN=500004;
    LL a[MAXN];
    LL Min[MAXN][25],gcd[MAXN][25];
    int n;
    int lg[MAXN];
    int ans[MAXN],top=0;
    
    inline LL read(){
        LL ret=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(f=='-') f=-f;c=getchar();}
        while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
        return ret*f;
    }
    
    inline LL min(LL a,LL b){
        return a<b?a:b;
    }
    
    inline LL Gcd(LL a,LL b){
        return (!b)?a:Gcd(b,a%b);
    }
    
    inline void prework(){
        lg[0]=-1;
        for(register int i=1;i<=n;++i){
            lg[i]=lg[(i>>1)]+1;
            Min[i][0]=a[i];
            gcd[i][0]=a[i];
        }
        for(register int k=1;k<=20;++k){
            for(register int i=1;i<=n-(1<<k)+1;++i){
                Min[i][k]=min(Min[i][k-1],Min[i+(1<<(k-1))][k-1]);
                gcd[i][k]=Gcd(gcd[i][k-1],gcd[i+(1<<(k-1))][k-1]);
            }
        }
    }
    
    inline bool check(int len){
        for(register int i=1;i<=n-len+1;++i){
            if(Gcd(gcd[i][lg[len]],gcd[i+len-(1<<lg[len])][lg[len]])==min(Min[i][lg[len]],Min[i+len-(1<<lg[len])][lg[len]]))
                return true;
        }
        return false;
    }
    
    inline void calc(int len){
        for(register int i=1;i<=n-len+1;++i){
            if(Gcd(gcd[i][lg[len]],gcd[i+len-(1<<lg[len])][lg[len]])==min(Min[i][lg[len]],Min[i+len-(1<<lg[len])][lg[len]]))
                ans[++top]=i;
        }
    }
    
    inline void solve(){
        int l=0,r=n;
        while(l<r){
            int mid=(l+r+1)>>1;
            if(check(mid)) l=mid;
            else r=mid-1;
        }
        calc(l);
        printf("%d %d
    ",top,l-1);
        for(register int i=1;i<=top;++i)
            printf("%d ",ans[i]);
    }
    
    int main(){
        freopen("point.in","r",stdin);
        freopen("point.out","w",stdout);
        scanf("%d",&n);
        for(register int i=1;i<=n;++i) a[i]=read();
        prework();
        solve();
        return 0;
    }
    View Code

    T3

    题目描述:

    因为NYG很老实,于是他又对放入背包的序列开始了思考。
    由于NYG很擅长序列问题,他的一大爱好就是对这些序列进行拆分。
    一个长为n的正整数序列A,对 于≤ i ≤ n都有Ai ∈ [l, r],NYG定义它的一个拆分为一个
    长为k的序列S,满足:
    1.S1=1
    2. Sk=n+1
    3.Si < Si+1, 1 ≤ i < k
    NYG认为,一个拆分是优秀(高贵)的,当且仅当对于每一个i, 1 ≤ i < k,A中的元素
    ASi , ASi+1, . . . , ASi+1-1构成等比数列。
    给出n, l, r,NYG要你求出所有可能的(r-l+1)的n次方的序列
    的优秀拆分的个数总和。由
    于答案可能很大,输出对1e9+7取模。
    NYG觉得这样的拆分实在是太多了,所以就把任务扔给了你。

    样例输入:

    4
    1 1 2
    10 6 6
    3 1 4
    100 1000 100000

    样例输出:

    2

    512

    198

    540522901

    解析:

    首先考虑dp方程:设cnt[i]为以i为长度的区间的等比数列的个数,dp[i]为已经填了i位的拆分总数。

    显然dp[i]=dp[i-1]*cnt[1]+dp[i-2]*cnt[2]+...+dp[0]*cnt[n];

    这个式子显然可以用矩阵快速幂优化。那么问题就变成了怎么快速处理cnt数组。

    由于公比一定是有理数,不妨设其为$frac{x}{y}$,由于公比大于1的等比数列的个数跟小于1的数量是完全相同的,不妨设下x>y。设等比数列长为n,则an=$frac{x^{n-1}}{y^{n-1}}a1$。
    不妨令t=$frac{x^{n-1}}{y^{n-1}}a1$.。注意到一旦x.y确定,由于$a_{n}=t imes x^{n-1}leq r,a_{1}=t imes y^{n-1}geq l$。
    合法的个数为$left lfloor frac{r}{x^{n-1}} ight floor-left lfloor frac{l-1}{y^{n-1}} ight floor$与0取max。
    在sqrt(r)内枚举x,y即可。但注意到没有统计公比为1的答案。此时dp方程变为了dp[i]=(dp[i-1]*cnt[1]+dp[i-2]*(cnt[2]+r-l+1)+...+dp[0]*(cnt[n]+r-l+1));
    在矩阵快速幂时多记录一个前缀和即可。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    using namespace std;
    
    typedef long long LL;
    const int mod=1e9+7;
    LL n;
    int l,r,T;
    LL cnt[35];
    struct Matrix{
        LL m[35][35];
        Matrix(){
            for(int i=1;i<=27;++i)
                for(int j=1;j<=27;++j)
                    m[i][j]=0LL;
        }
    }dw;
    
    int gcd(int x,int y){
        return (!y)?x:gcd(y,x%y);
    }
    
    void prework(){
        for(int i=0;i<=25;++i) cnt[i]=0;
        cnt[1]=r-l+1;
        cnt[2]=1LL*(r-l+1)*(r-l)%mod;
        int lim=sqrt(r)+1;
        for(int x=1;x<=lim;++x)
            for(int y=1;y<x;++y)
                if(gcd(x,y)==1){
                    int xx=x,yy=y;
                    for(int k=2;1LL*xx*x<=r;++k)
                        xx*=x,yy*=y,cnt[k+1]+=1LL*max(0,r/xx-(l-1)/yy);
                }
        for(int i=3;i<=25;++i) cnt[i]=(cnt[i]<<1)%mod;
    }
    
    Matrix mul(Matrix A,Matrix B){
        Matrix C;
        for(int i=1;i<=27;++i)
            for(int j=1;j<=27;++j)
                for(int k=1;k<=27;++k)
                    C.m[i][j]=(C.m[i][j]+A.m[i][k]*B.m[k][j]%mod)%mod;
        return C;
    }
    
    Matrix qpow(Matrix x,LL y){
        Matrix res=dw;
        while(y){
            if(y&1) res=mul(res,x);
            x=mul(x,x);
            y>>=1;
        }
        return res;
    }
    
    void work(){
        Matrix A;
        A.m[1][1]=1;
        Matrix B;
        for(int i=1;i<=25;++i) B.m[i][1]=cnt[i];
        B.m[27][1]=1LL*(r-l+1);
        for(int i=1;i<=25;++i) B.m[i][i+1]=1LL;
        B.m[1][27]=1LL;
        B.m[27][27]=1LL;
        B=qpow(B,n);
        printf("%lld
    ",mul(A,B).m[1][1]%mod);
    }
    
    int main(){
        freopen("excellent.in","r",stdin);
        freopen("excellent.out","w",stdout);
        scanf("%d",&T);
        for(int i=1;i<=27;++i) dw.m[i][i]=1;
        while(T--){
            scanf("%lld%d%d",&n,&l,&r);
            prework();
            work();
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Duff and Meat(贪心)
    Duff and Meat(贪心)
    Eugeny and Array(水题,注意题目描述即可)
    Eugeny and Array(水题,注意题目描述即可)
    HDU-2588-GCD (欧拉函数)
    HDU-2588-GCD (欧拉函数)
    再谈欧拉函数
    再谈欧拉函数
    容斥定理及浅略介绍
    Vue
  • 原文地址:https://www.cnblogs.com/JoshDun/p/11517173.html
Copyright © 2011-2022 走看看