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

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)

明早继续解释。

原文地址:https://www.cnblogs.com/opethrax/p/11372825.html