2018.10.23模拟赛

T1 count

题目:给出 m m m 个数 a [ 1 ] , a [ 2 ] , … , a [ m ] a[1],a[2],…,a[m] a[1],a[2],,a[m] 1 − n 1-n 1n 中有多少数不是 a [ 1 ] , a [ 2 ] , … , a [ m ] a[1],a[2],…,a[m] a[1],a[2],,a[m]的倍数。

solution:

一看就是容斥嘛,就是注意一下乘的是 l c m lcm lcm就好了,水过水过

(很有意思的一点是有位同学直接拍大数据没错但是交上去 20 20 20分因为没有拍小数据

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
int n,m,a[25],f[(1<<20)+5];
LL sum[(1<<20)+5],ans;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline int calc(int x){
	int ret=0;
	while(x){
		if(x&1) ret++; x>>=1;
	} return ret;
}

inline LL gcd(LL a,LL b){
	return b==0?a:gcd(b,a%b);
}

inline void prework(){
	for(int i=1;i<(1<<m);i++){
		f[i]=calc(i); sum[i]=1;
		for(int j=1;j<=m;j++)
			if(i&(1<<(j-1)))
				sum[i]=1LL*sum[i]*a[j]/gcd(sum[i],a[j]);
	}
}

int main(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=m;i++) a[i]=rd();
	sort(a+1,a+m+1); m=unique(a+1,a+m+1)-a-1;
	prework(); int h;
	for(int i=1;i<(1<<m);i++){
		if(f[i]&1) h=1;
		else h=-1;
		ans=ans+1LL*h*n/sum[i];
	}
	ans=n-ans;
	printf("%lld\n",ans);
	return 0;
}

T2 kth

题目:一个区间的价值定义为该区间中的最大值减最小值 给定 n n n 个数,求所有区间价值中,第 k k k 大值为多少。

solution:

二分+单调队列,二分答案 a n s ans ans,统计有多少个区间的价值大于等于 a n s ans ans 区间 [ l − 1 , r ] [l-1,r] [l1,r]的价值一定大于等于 [ l , r ] [l,r] [l,r],所以对于每一右端点 i i i,必定存在一个阀值 K i Ki Ki 使得对于所有的 l &lt; = K i [ l , i ] l&lt;=Ki[l,i] l<=Ki[l,i]的价值必定大于等于 a n s ans ans 且随之 i i i 的增大, K i Ki Ki 也必定单调不降 用两个单调队列来维护阀值 K K K 的移动(一个维护最小值,一个维护最大值,当 K K K 增加时判 定最大值-最小值是否大于等于 a n s ans ans 即可) 时间复杂度 O ( l o g 2 1 e 9 ∗ n ) O(log_21e9*n) O(log21e9n)

这道题考试的时候想到了二分和类似单调队列或者单调栈这种解法但是感觉非常难调又没什么时间了所以就写了个 30 30 30分的暴力 Q A Q QAQ QAQ

放上正解代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define inf 0x3f3f3f3f
#define maxn 400005
#define int long long
using namespace std;
int n,k,a[maxn],mn[maxn],mx[maxn],l,r,ans;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return f*x;
}

inline bool check(int val){
	int cnt=0,l1=1,r1=1,l2=1,r2=1,now=1,pos;
	mn[1]=1,mx[1]=1;
	while(now<=n){
		pos=min(mn[l1],mx[l2]);
		while(pos<now){
			pos++;
			while(l1<r1 && mn[l1]<pos && a[mx[l2]]-a[mn[l1+1]]>=val) l1++;
			while(l2<r2 && mx[l2]<pos && a[mx[l2+1]]-a[mn[l1]]>=val) l2++;
			if(mn[l1]<pos || mx[l2]<pos) break;
		}
		pos--; now++;
		if(a[mx[l2]]-a[mn[l1]]>=val) cnt+=pos; 
		while(l1<=r1 && a[mn[r1]]>a[now]) r1--;
		mn[++r1]=now;
		while(l2<=r2 && a[mx[r2]]<a[now]) r2--;
		mx[++r2]=now;
		if(cnt>=k) return true;
	}
	return cnt>=k;
}

signed main(){
	freopen("kth.in","r",stdin);
	freopen("kth.out","w",stdout);
	n=rd(); k=rd();
	l=inf,r=-inf;
	for(int i=1;i<=n;i++) 
		a[i]=rd(),l=min(l,a[i]),r=max(r,a[i]);
	r=r-l+1,l=0;
	while(r>l){
		int mid=(l+r)>>1;
		if(check(mid)) l=mid+1,ans=mid;
		else r=mid;
	}
	printf("%lld\n",ans);
	return 0;
}

T3 submax

题目:有 n n n 个堡垒排成一排构成了一条防御线。现在需要将 n n n 个武器放入这 n n n 个堡垒中,每个 堡垒放一个,每个武器有攻击力和战场贡献值两个属性。 由于这 n n n 个武器都不是人为操控的,所以会对其某半径内所有单位进行攻击,而这就导 致某些堡垒的互相攻击。现在发现第 i i i 个堡垒会和第 j j j 个堡垒互相攻击当且仅当 ∣ i − j ∣ ≤ r |i-j|\le r ijr, 且攻击力较低的武器和他所在的堡垒会破损。 现在你需要给出一种武器分配方案使得未破损武器的战场贡献值总和最大。为了方便你 只需输出战场贡献值总和的最大值即可。

solution:

一道不那么容易一眼秒并且细节比较多的 d p dp dp

首先观察到攻击高的可以覆盖一段区间,所以就按攻击值从大到小排序考虑如何将他们填进去

f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个武器已经覆盖了 j j j的堡垒(我这里的 j j j是不包含最右端的武器向右覆盖的因为这样比较好处理),转移有三种: 1. 1. 1.放在当前最右的武器的右边,这样覆盖的区间会增加 1 1 1 2. 2. 2.放在前面被覆盖的区间内空下的一个城堡里,这样区间不会增加。 3. 3. 3.放在最右再往右 r r r的位置,此时这个点的贡献会加到答案里,并且覆盖区间会增加 r + 1 r+1 r+1

考场上 A ​ A​ A掉了,放上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 5005
using namespace std;
int t,n,r,f[maxn][maxn];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f; 
}

struct qwq{
	int val,w;
	bool operator <(const qwq &x) const{
		return val>x.val||(val==x.val&&w>x.w);
	}
}a[maxn];
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}

int main(){
	freopen("submax.in","r",stdin);
	freopen("submax.out","w",stdout);
	t=rd();
	while(t--){
		n=rd(); r=rd();
		for(int i=1;i<=n;i++) a[i].val=rd();
		for(int i=1;i<=n;i++) a[i].w=rd();
		sort(a+1,a+n+1); memset(f,0,sizeof f);
		f[1][1]=a[1].w;
		for(register int i=2;i<=n;i++)
			for(register int j=i;j<=min(n,(i-1)*(r+1)+1);j++){
				f[i][j]=f[i-1][j-1];
				f[i][j]=max(f[i][j],f[i-1][j]);
				if(j-r>=i) f[i][j]=max(f[i][j],f[i-1][j-r-1]+a[i].w);
			}
		printf("%d\n",f[n][n]);
	}
	return 0;
}

100 + 30 + 100 = 230 100+30+100=230 100+30+100=230

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值