【模拟赛】纪中提高A组 19.8.18 测试

Posted on 2019-08-18 19:33  opethrax  阅读(...)  评论(... 编辑 收藏

Task.1 完全背包

题目大意:物品 \(n\) 件,背包体积 \(m\),物品体积 \(a_i\) 价值 \(b_i\),每件物品无数件。求不超过容量上限的最大价值。

数据范围:\(1\leq N\leq 10^6,1\leq M\leq 10^{16},a_i,b_i\leq 100\)

题如其名,真的是完全背包。观察数据范围后发现对于 \(a_i,b_i\)\(a_i\) 相同的明显可以舍弃 \(b_i\) 小的,物品数量下降至 \(100\)。比较容易想到在一定范围内,贪心地选取单位体积价值大的物品,小范围内做背包。至于这个范围的大小题解给做出了相关讨论:

引理:给定任意 \(n\) 个整数,它们之中存在若干个整数的和为 \(n\) 的倍数。

定理:假设贪心的选取若干件性价比最高的物品,此外选择 \(x\) 件其他的物品是最优的情况,那么存在一种最优情况使得 \(x\leq val_{max}\)

至于证明?想想为什么。


此外还存在一种矩阵优化普通的 \(dp\) 的做法。

通常的矩阵加速递推是形如 \(f_i=\sum^{i-1}_{j=i-k} c_{i-j}\cdot f_j\) 的形式,用普通的矩乘就能优化。对于 \(\min/\max\) 一类的操作,他们和加法具有类似的性质。如果定义一种矩阵的取 \(\max\) 运算(\(A\ \max\ B=C,c_{i,j}=max\{max(a_{i,k},b_{k,j})\}\)),这种运算满足结合律,也可以用矩乘快速幂优化。

代码请转至:https://www.cnblogs.com/15owzLy1-yiylcy/protected/p/11379610.html

鸣谢 \(15owzly1\)

Task.2 中间值

题目大意:有两个长度均为 \(n\) 的单增不降序列 \(a,b\),给出 \(m\) 次修改和查询操作。每次修改操作给出参数 \(s,x,y\) ,表示修改 \(a\ [s=0]\)\(b\ [s=1]\) 中的位置 \(x\) 上的值为 \(y\),保证修改操作不破坏单增不降的性质。查询操作给出区间 \([l_1,r_1],[l_2,r_2]\),询问两段区间合并后的 \(k\) 中位数,保证查询操作两区间长度和为奇数。

数据范围:\(1\leq N\leq 5\cdot 10^5,1\leq M\leq 10^6,0\leq a_i,b_i,y\leq 10^9,1\leq l_1\leq r_1\leq N,1\leq l_2\leq r_2\leq N\)

修改操作可以 \(O(1)\)

可以想到一个二分的做法:二分一个数字作为中位数,每次在两段区间中二分这个数的位置,判断答案是否合法。单次查询复杂度 \(O(\log^2 N)\)。期望得分 \(70\) (还要卡一卡,所以我没写)。

考虑换个二分的对象,二分答案在一个序列中的位置,再根据位置去另一个序列的期望位置判断合不合法,这样单次就只有 \(O(\log N)\)

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

template<class T>void read(T &x){
    x=0; char c=getchar();
    while(c<'0'||'9'<c)c=getchar();
    while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
}

const int N=500050;

int n,m;
int a[N],b[N];
int inf=1e9+1,fni=-1e9-1;

int solve(int La,int Ra,int Lb,int Rb){
    int l=La,r=Ra,mid;
    int len=(Ra+Rb-La-Lb+2)>>1,pos;
    swap(b[Lb-1],fni); swap(b[Rb+1],inf);
    while(l<=r){
        mid=(l+r)>>1;
        pos=Lb+len-(mid-La+1);
        if(pos+1<Lb){r=mid-1; continue;}
        if(pos-1>Rb){l=mid+1; continue;}
        if(b[pos]<=a[mid]&&a[mid]<=b[pos+1]){
            swap(b[Lb-1],fni); swap(b[Rb+1],inf);
            return a[mid];
        }
        if(b[pos]>a[mid])l=mid+1;
        else r=mid-1;
    }
    swap(b[Lb-1],fni); swap(b[Rb+1],inf);
    
    l=Lb; r=Rb;
    swap(a[La-1],fni); swap(a[Ra+1],inf);
    while(l<=r){
        mid=(l+r)>>1;
        pos=La+len-(mid-Lb+1);
        if(pos+1<La){r=mid-1; continue;}
        if(pos-1>Ra){l=mid+1; continue;}
        if(a[pos]<=b[mid]&&b[mid]<=a[pos+1]){
            swap(a[La-1],fni); swap(a[Ra+1],inf);
            return b[mid];
        }
        if(a[pos]>b[mid])l=mid+1;
        else r=mid-1;
    }
    swap(a[La-1],fni); swap(a[Ra+1],inf);
}

int main(){
//  freopen("median.in","r",stdin);
//  freopen("median.out","w",stdout);
    read(n); read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++) read(b[i]);
    int opt,x,y,l,r;
    while(m--){
        read(opt); read(x); read(y); read(l);
        if(opt==1){if(x)b[y]=l;else a[y]=l;}
        else {
            read(r);
            printf("%d\n",solve(x,y,l,r));
        }
    }
    return 0;
}

还有一种思路是每次在每个区间取中间值,然后比较大小分类讨论扔掉一部分区间。

Task.3 Sequence

题目大意:定义序列 \(A\) 的价值为 \(gcd(a_1,a_2,a_3,...a_n,B)\)\(B\) 为给出的定值。定义 \(f(i)\) 为所有的长度为 \(n\),由 \(i\) 的约数组成的序列的价值和。给出 \(n,m,B\),求 \(\sum^m_{i=1} f(i)\)

数据范围:\(N,B\leq 10^{18},M\leq 2\cdot 10^7\)

考虑某一个 \(i\) 的约数组成的序列,若 \(i\)\(c_i\) 个约数,\(i\) 对应得序列数量为 \(c_i^n\),每个序列的价值一定是 \(B\) 的约数。

对于一个质数 \(p\)\(p\) 对应的序列可能的价值取值只有;两种:\(1\)\(p\) (如果 \(p\)\(B\) 的约数),\(1\)

明早继续解释。