例一: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
1≤n≤2000 ,
−
1
0
9
≤
x
i
,
y
i
,
w
i
≤
1
0
9
−10^9≤x_i,y_i,w_i≤10^9
−109≤xi,yi,wi≤109
分析
首先将纵坐标离散化到 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);
}