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\n");
                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\n");
                flag=true;
                break;
            }
            h+=1LL*s2[i].b;
        }
        if(flag) continue;
        printf("Yes\n");
    }
    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\n",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\times x^{n-1}\leq r,a_{1}=t\times y^{n-1}\geq l$。
合法的个数为$\left \lfloor \frac{r}{x^{n-1}} \right \rfloor-\left \lfloor \frac{l-1}{y^{n-1}} \right \rfloor$与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\n",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