Task.1 完全背包
题目大意:物品 (n) 件,背包体积 (m),物品体积 (a_i) 价值 (b_i),每件物品无数件。求不超过容量上限的最大价值。
数据范围:(1leq Nleq 10^6,1leq Mleq 10^{16},a_i,b_ileq 100)
题如其名,真的是完全背包。观察数据范围后发现对于 (a_i,b_i),(a_i) 相同的明显可以舍弃 (b_i) 小的,物品数量下降至 (100)。比较容易想到在一定范围内,贪心地选取单位体积价值大的物品,小范围内做背包。至于这个范围的大小题解给做出了相关讨论:
引理:给定任意 (n) 个整数,它们之中存在若干个整数的和为 (n) 的倍数。
定理:假设贪心的选取若干件性价比最高的物品,此外选择 (x) 件其他的物品是最优的情况,那么存在一种最优情况使得 (xleq 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) 大 中位数,保证查询操作两区间长度和为奇数。
数据范围:(1leq Nleq 5cdot 10^5,1leq Mleq 10^6,0leq a_i,b_i,yleq 10^9,1leq l_1leq r_1leq N,1leq l_2leq r_2leq 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
",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,Bleq 10^{18},Mleq 2cdot 10^7)
考虑某一个 (i) 的约数组成的序列,若 (i) 有 (c_i) 个约数,(i) 对应得序列数量为 (c_i^n),每个序列的价值一定是 (B) 的约数。
对于一个质数 (p),(p) 对应的序列可能的价值取值只有;两种:(1) 和 (p) (如果 (p) 是 (B) 的约数),(1) 。
明早继续解释。