一些想法题

例一:Longest Subarray [线段树]

链接

HDOJ 6602 http://acm.hdu.edu.cn/showproblem.php?pid=6602

题意

N个数字,范围为[1,C],求最长的连续子序列,
使得子序列中每种数值出现的次数大于等于K或者等于0。
N,C,K≤ 1 0 5 10^5 105

分析

如果右端点固定,对于每种元素,可行的左端点下标是两段连续的区间。
对于每种元素,将它的可行左端点区间在线段树中加一。
当右端点右移的时候,维护C 种元素的可行左端点。
查询时只需要询问线段树中最小的、值为C 的下标即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
const int M=1e5+5;
int n,c,k,A[M];
vector<int>vi[M];
int mx[M<<2],lazy[M<<2];
void build(int l=1,int r=n,int p=1){
	mx[p]=c,lazy[p]=0;
	if(l==r)return;
	int mid=l+r>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
}
void down(int p){
	if(lazy[p]){
		mx[p<<1]+=lazy[p];
		mx[p<<1|1]+=lazy[p];
		lazy[p<<1]+=lazy[p];
		lazy[p<<1|1]+=lazy[p];
		lazy[p]=0;
	}
}
int query(int l=1,int r=n,int p=1){
	if(l==r)return l;//n==1时特判
	down(p);
	int mid=l+r>>1;
	if(mx[p<<1]==c)return query(l,mid,p<<1);
	else if(mx[p<<1|1]==c)return query(mid+1,r,p<<1|1);
	else return n+1;
}
void update(int a,int b,int v,int l=1,int r=n,int p=1){
	if(l>b||r<a)return;
	if(l>=a&&r<=b){
		mx[p]+=v;
		lazy[p]+=v;
		return;
	}
	down(p);
	int mid=l+r>>1;
	update(a,b,v,l,mid,p<<1);
	update(a,b,v,mid+1,r,p<<1|1);
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	while(scanf("%d%d%d",&n,&c,&k)!=EOF){
		for(int i=1;i<=n;i++)rd(A[i]);
		for(int i=1;i<=c;i++)vi[i].clear(),vi[i].push_back(0);
		build();
		int ans=0;
		for(int i=1;i<=n;i++){
			vi[A[i]].push_back(i);
			int sz=vi[A[i]].size();
			if(sz-k-1>=0)update(vi[A[i]][sz-k-1]+1,vi[A[i]][sz-k],+1);
			update(vi[A[i]][sz-2]+1,vi[A[i]][sz-1],-1);
			MAX(ans,i-query()+1);
		}
		printf("%d\n",ans);
	}
	return (0-0);
}

例二:Snowy Smile [线段树]

链接

HDOJ 6638 http://acm.hdu.edu.cn/showproblem.php?pid=6638

题意

有n个点,给出每个点的下标 ( x i , y i ) (x_i,y_i) (xi,yi)和价值 w i w_i wi
选出一个最大的矩形,使得矩形内(包含边界)的所有点的权值和最大。
1 ≤ n ≤ 2000 1≤n≤2000 1n2000 − 1 0 9 ≤ x i , y i , w i ≤ 1 0 9 −10^9≤x_i,y_i,w_i≤10^9 109xi,yi,wi109

分析

首先将纵坐标离散化到 O(n) 的范围内,方便后续的处理。 将所有点按照横坐标排序,枚举矩形的上边界,然后往后依次加入每个点,这样就确定了 矩形的上下边界。设 v[y] 表示矩形内部纵坐标为 y 的点的权值和,则答案为 v 的最大子段和, 用线段树维护带修改的最大子段和即可。 时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
const int M=2005;
struct node{
	int x,y,val;
	bool operator <(const node &A)const{
		return x<A.x;
	}
}A[M];
int cas,n,unix,uniy,X[M],Y[M];
ll ans,mx[M<<2],lmx[M<<2],rmx[M<<2],sum[M<<2];
void build(int l=1,int r=uniy,int p=1){
	mx[p]=lmx[p]=rmx[p]=sum[p]=0;
	if(l==r)return;
	int mid=l+r>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
}
void insert(int x,int v,int l=1,int r=uniy,int p=1){
	if(l>x||r<x)return;
	if(l==r){
		mx[p]+=v;
		lmx[p]+=v;
		rmx[p]+=v;
		sum[p]+=v;
		return;
	}
	int mid=l+r>>1;
	insert(x,v,l,mid,p<<1);
	insert(x,v,mid+1,r,p<<1|1);
	sum[p]=sum[p<<1]+sum[p<<1|1];
	mx[p]=max(rmx[p<<1]+lmx[p<<1|1],max(mx[p<<1],mx[p<<1|1]));
	lmx[p]=max(lmx[p<<1],sum[p<<1]+lmx[p<<1|1]);
	rmx[p]=max(rmx[p<<1|1],sum[p<<1|1]+rmx[p<<1]);
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	rd(cas);
	while(cas--){
		ans=unix=uniy=0;
		rd(n);
		for(int i=1;i<=n;i++){
			rd(A[i].x),rd(A[i].y),rd(A[i].val);
			X[++unix]=A[i].x;
			Y[++uniy]=A[i].y;
		}
		sort(A+1,A+1+n);
		sort(X+1,X+1+unix);
		sort(Y+1,Y+1+uniy);
		unix=unique(X+1,X+1+unix)-X-1;
		uniy=unique(Y+1,Y+1+uniy)-Y-1;
		for(int i=1;i<=n;i++){
			A[i].x=lower_bound(X+1,X+1+unix,A[i].x)-X;
			A[i].y=lower_bound(Y+1,Y+1+uniy,A[i].y)-Y;
		}
		for(int i=1;i<=unix;i++){
			int now=1;
			while(now<=n&&A[now].x<i)now++;
			build();
			for(int j=i;j<=unix;j++){
				while(now<=n&&A[now].x==j)insert(A[now].y,A[now].val),now++;
				MAX(ans,mx[1]);
			}
		}
		printf("%lld\n",ans);
	}
	return (0-0);
}

例三:Final Exam [想法题]

链接

HDOJ 6651 http://acm.hdu.edu.cn/showproblem.php?pid=6651

题意

一场考试中有n门课程,总分为m。通过一门分数为x的课程,你需要x+1的时间。
求最少的复习时间,使得无论n门课程的分值如何安排,你都能至少通过k门课程。

分析

换位思考, 考虑如果我们是出题人会怎么让学生做不出 k 题, 即最坏情况.显然, 我们会挑出学 生复习得最少的 n−k + 1 道题, 让每道题的难度都等于他复习的时间.(田忌赛马的策略) 因此, 回到学生视角, 我们要让自己复习的最少的 n−k + 1 题复习时间总和 > m, 构造方式显然

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
int cas,n,m,k;
int main(){
#ifndef ONLINE_JUDGE
	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	rd(cas);
	while(cas--){
		rd(n),rd(m),rd(k);
		printf("%lld\n",1ll*(m/(n-k+1)+1)*(k-1)+m+1);
	}
	return (0-0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值