标签:ems set can 差分 itemid neu 最大 集合 turn
题意:给出一段数列,让你求[L,R]区间内第k大的数字
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int N = MAXN*40;
int n,m,q,tot;
int T[MAXN],A[MAXN],t[MAXN];
int lson[N],rson[N],sum[N];
vector<int>V;
int getid(int x) //离散化
{
return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
}
int build(int l,int r) //建立一棵空树
{
int rt = tot++;
sum[rt] = 0;
if(l!=r){
int mid=(l+r)>>1;
lson[rt] = build(l,mid);
rson[rt] = build(mid+1,r);
}
return rt;
}
int update(int rt,int pos) //把数组中的元素一次加入新的线段树中
{
int nrt = tot++;
int tmp = nrt;
sum[nrt] = sum[rt]+1;
int l=1,r=m;
while(l<r) {
int mid = (l+r)>>1;
if(pos<=mid) {
lson[nrt] = tot++;
rson[nrt] = rson[rt];
nrt = lson[nrt];
rt = lson[rt];
r = mid;
}else {
rson[nrt] = tot++;
lson[nrt] = lson[rt];
nrt = rson[nrt];
rt = rson[rt];
l=mid+1;
}
sum[nrt] = sum[rt]+1;
}
return tmp;
}
int query(int lrt,int rrt,int k)
{
int l=1,r=m;
while(l<r) {
int mid = (l+r)>>1;
int cnt = sum[lson[rrt]] - sum[lson[lrt]];
if(cnt>=k) {
r = mid;
lrt = lson[lrt];
rrt = lson[rrt];
} else {
l = mid+1;
k-=cnt;
lrt = rson[lrt];
rrt = rson[rrt];
}
}
return l;
}
int main()
{//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&q);tot=0;
for(int i=1;i<=n;i++) {
scanf("%d",&A[i]);
V.push_back(A[i]);
}
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
m=V.size();
T[0] = build(1,m);
for(int i=1;i<=n;i++) {
T[i] = update(T[i-1],getid(A[i]));
}
while(q--) {
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",V[query(T[x-1],T[y],k)-1]);
}
return 0;
}
题意:贴海报,海报可以覆盖,会给出你每张海报的长度及起始位置,然后问你最后还能看到几张海报。
#include<math.h>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=10005;
int n;
int vis[maxn<<3],sum[maxn<<4];
int li[maxn*2],ri[maxn*2],lsh[maxn<<2];
void pushdown(int rt)
{
sum[rt<<1]=sum[rt];
sum[rt<<1|1]=sum[rt];
sum[rt]=-1;
}
void update(int L,int R,int C,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
sum[rt]=C;
return ;
}
if(sum[rt]!=-1)
pushdown(rt);
int m=(l+r)>>1;
if(m>=R) update(L,R,C,l,m,rt<<1);
else if(L>m) update(L,R,C,m+1,r,rt<<1|1);
else update(L,m,C,l,m,rt<<1),update(m+1,R,C,m+1,r,rt<<1|1);
}
int ans;
void query(int l,int r,int rt)
{
if(!vis[sum[rt]]&&sum[rt]!=-1)
{
ans++;
vis[sum[rt]]=1;
return ;
}
if(l==r)
{
return ;
}
if(sum[rt]!=-1)
pushdown(rt);
int m=(l+r)>>1;
query(l,m,rt<<1);
query(m+1,r,rt<<1|1);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
memset(sum,-1,sizeof(sum));
memset(vis,0,sizeof(vis));
int tot=0;
for(int i=0;i<n;i++)
{
scanf("%d%d",&li[i],&ri[i]);
lsh[tot++]=li[i];
lsh[tot++]=ri[i];
}
sort(lsh,lsh+tot);
int mm=unique(lsh,lsh+tot)-lsh;
int tt=mm;
for(int i=1;i<tt;i++)
{
if(lsh[i]-lsh[i-1]>1)
lsh[mm++]=lsh[i-1]+1;
}
sort(lsh,lsh+mm);
for(int i=0;i<n;i++)
{
int x=lower_bound(lsh,lsh+mm,li[i])-lsh;
int y=lower_bound(lsh,lsh+mm,ri[i])-lsh;
update(x,y,i,0,mm-1,1);
}
ans=0;
query(0,mm-1,1);
printf("%d\n",ans);
}
}
题意:找到一段数字里最大值和最小值的差
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX_N = 5e4 + 5;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> P;
P dat[4 * MAX_N];//存储线段树的全局数组
int n;
//初始化
void init(int N) {
n = 1;
while (n < N) n <<= 1;//简单起见,把元素个数扩大到2的幂
for (int i = 0; i < 2 * n - 1; ++i) {
dat[i].first = INF;//存储区间最小值
dat[i].second = -INF;//存储区间最大值
}
}
//把第k个值更新为x
void update(int k, int x) {
k += n - 1;
dat[k] = P(x, x);
while (k > 0) {//向上更新
k = (k - 1) / 2;
dat[k].first = min(dat[2 * k + 1].first, dat[2 * k + 2].first);
dat[k].second = max(dat[2 * k + 1].second, dat[2 * k + 2].second);
}
}
//查询
P query(int a, int b, int k, int l, int r) {//k是节点编号
if (a <= l && r <= b) return dat[k];
if (a > r || b < l) return P(INF, -INF);
P vl = query(a, b, 2 * k + 1, l, (l + r) / 2);
P vr = query(a, b, 2 * k + 2, (l + r) / 2 + 1, r);
return P(min(vl.first, vr.first), max(vl.second, vr.second));
}
int main() {
int N, Q;
scanf("%d%d", &N, &Q);
init(N);
for (int i = 0; i < N; ++i) {
int x;
scanf("%d", &x);
update(i, x);
}
for (int i = 0; i < Q; ++i) {
int a, b;
scanf("%d%d", &a, &b);
P p = query(a - 1, b - 1, 0, 0, n - 1);
printf("%d\n", p.second - p.first);
}
return 0;
}
还有一种基于稀疏表的 RMQ 的方法
预处理: 预处理是采用dp的思想,用f[i][j]表示区间[i,i+2^j-1]中的最大值 (即从i开始,长度为2^j的闭区间)。开始时,f[i][0] 就是区间[i][i]的值, 即f[i][0] = num[i],好了,初始值找到了,下面是状态转移方程: f[i][j] = max (f[i][j-1],f[i+2^(j-1)][j-1])。即把[i,i+2^j-1]区间分为 [i,i+2^(j-1)-1]和[j+2^(j-1),j+2^(j-1)+2^(j-1)-1]两个等长度的区间(区间长度都是2^(j-1)), 有了初始值和状态转移方程,我们可以自底向上递推出所有的f[i][j]的值。 边界值的处理: 由于区间最大长度为n,所以二维边界最大值为log(n)/log(2.0); 一维边界为i+2^j-1<=n。 查询: 假设要查询区间[a,b]的最大值,由于区间的长度很可能不是2的整数幂,所以 我们要把区间划分为长度为2的整数幂的两部分,而且这两个的并集必须是[a,b]。 为了实现这个方案,我们需要先求出一个最大k,使得2^k<=(b-a+1),这样就可以把 区间分为两部分[a,a+2^k-1]和[b-2^k+1,b],使它们既能不超过[a,b]区间的范围,又能 把区间全部覆盖。于是,[a,b]区间的最大值就等于上述两个区间的最大值中最大的那个。
#include <stdio.h>
#include <math.h>
#define MAXN 100
#define max(a,b) (a > b ? a : b)
const int num[MAXN];
int dp[MAXN][20];
void create_max (int n) {
int i,j,t;
for (i = 1;i <= n;i++) {
dp[i][0] = num[i];
}
t = (int)(log(n) / log(2.0));
for (j = 1;j <= t;j++) {
for (i = 1;i + (1 << j) - 1 <= n;i++) {
dp[i][j] = max(dp[i][j-1],dp[i+(1 << (j-1))][j-1]);
}
}
}
int get_max (int a,int b) {
int k = log(b - a + 1) / log(2.0);
return max(dp[a][k],dp[b-(1 << k) + 1][k]);
}
int main () {
int i,j,n,ans,a,b;
scanf ("%d",&n);
for (i = 1;i <= n;i++) {
scanf ("%d",num+i);
}
create_max (n);
scanf ("%d%d",&a,&b);
ans = get_max (a,b);
printf ("%d\n",ans);
return 0;
}
题意:一个不降序列,让我们去区间询问区间最长连续相等序列。
// #include<bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring> // for memset
#include <vector> // push_back() // vector<int>().swap(v);
#include <set> //multiset set<int,greater<int>> //big->small
#include <map>
#include <stack> // top()
#include <queue> // front() // priority_queue<T,vector<T>,greater<T> >
#include <cmath> // auto &Name : STLName Name.
#include <utility>
#include <sstream>
#include <string> // __builtin_popcount (ans); // 获取某个数二进制位1的个数
#include <cstdlib> // rand()
#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;
const int maxn = 100010;
int num[maxn],cnt[maxn],dp[maxn][25];
int n,q,x,y;
void ST(int n)
{
for(int i=1;i<=n;i++)
dp[i][0]=cnt[i];
for(int j=1;j<=24;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int query(int i,int j)
{
int k=log(j-i+1.0)/log(2.0);
return max(dp[i][k],dp[j-(1<<k)+1][k]);
}
int main(void)
{
while(~scanf("%d%d",&n,&q)&&n)
{
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if(i==1) cnt[i]=1;
else if(num[i]==num[i-1]) cnt[i]=cnt[i-1]+1;
else cnt[i]=1;
}
ST(n);
while(q--)
{
scanf("%d%d",&x,&y);
int now=x;
while(now<=y&&num[now-1]==num[now])
now++;
int ans=0;
if(now<=y)
ans=query(now,y);
ans=max(ans,now-x);
printf("%d\n",ans);
}
}
return 0;
}
题意:一个整数集合Z有n个区间,每个区间有3个值,ai,bi,ci代表,在区间[ai,bi]上至少有ci个整数属于集合Z,ci可以在区间内任意取不重复的点。现在要满足所有区间的约束条件,问最少可选多少个点。
思路:差分约束
// #include<bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring> // for memset
#include <vector> // push_back() // vector<int>().swap(v);
#include <set> //multiset set<int,greater<int>> //big->small
#include <map>
#include <stack> // top()
#include <queue> // front() // priority_queue<T,vector<T>,greater<T> >
#include <cmath> // auto &Name : STLName Name.
#include <utility>
#include <sstream>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;
const int maxn = 50005;
const int inf = 0x3f3f3f3f;
struct Edge
{
int v;
int cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge> E[maxn];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[maxn];
int cnt[maxn];
int dist[maxn];
bool SPFA(int start,int n,int *dist)
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++) dist[i]=-inf;
vis[start]=true;
dist[start]=0;
queue<int>que;
while(!que.empty()) que.pop();
que.push(start);
memset(cnt,0,sizeof(cnt));
cnt[start]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
vis[u]=false;
for(int i=0;i<E[u].size();i++)
{
int v=E[u][i].v;
if(dist[v]<dist[u]+E[u][i].cost)
{
dist[v]=dist[u]+E[u][i].cost;
if(!vis[v])
{
vis[v]=true;
que.push(v);
if(++cnt[v]>n) return false;
}
}
}
}
return true;
}
void init()
{
for(int i=0;i<maxn;i++)
E[i].clear();
}
int main(void)
{
int n;
while(~scanf("%d",&n))
{
init();
int a,b,c,s=inf,e=-1;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
addedge(a,b+1,c);
s=min(s,a);
e=max(e,b+1);
}
for(int i=s;i<e;i++)
{
addedge(i,i+1,0);
addedge(i+1,i,-1);
}
SPFA(s,e,dist);
printf("%d\n",dist[e]);
}
return 0;
}
还有一种方法就是贪心算法,用线段树优化
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MMAX 50005
using namespace std;
int sum[MMAX*4],setv[MMAX*4];
int t,_sum;
struct node
{
int l,r,v;
} line[MMAX];
bool cmp(node a,node b)
{
return a.r<b.r;
}
void build(int rt,int L,int R)
{
if(L==R)
{
sum[rt]=1;
}
else
{
int M=(L+R)/2;
build(rt*2,L,M);
build(rt*2+1,M+1,R);
sum[rt]=sum[rt*2]+sum[rt*2+1];
}
}
void update(int rt,int L,int R,int ql,int qr)
{
if(t==0) return ;
// if(sum[rt]==0) return ;
if(ql<=L&&qr>=R&&sum[rt]&&t>=sum[rt])
{
t-=sum[rt];
sum[rt]=0;
return ;
}
if(L==R) return ;
if(sum[rt]==0)
{
int lc=rt*2,lr=rt*2+1;
sum[lr]=sum[lc]=0;
}
int M=(L+R)/2;
if(qr>M) update(rt*2+1,M+1,R,ql,qr);
if(ql<=M) update(rt*2,L,M,ql,qr);
sum[rt]=sum[rt*2+1]+sum[rt*2];
}
void query(int rt,int L,int R,int ql,int qr)
{
if(sum[rt]==0) return ;
if(ql<=L&&qr>=R)
{
_sum+=sum[rt];
}
else
{
if(sum[rt]==0)
{
int lc=rt*2,lr=rt*2+1;
sum[lr]=sum[lc]=0;
}
int M=(L+R)/2;
if(ql<=M) query(rt*2,L,M,ql,qr);
if(qr>M) query(rt*2+1,M+1,R,ql,qr);
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(setv,-1,sizeof(setv));
int Max=0;
for(int i=0; i<n; i++)
{
scanf("%d%d%d",&line[i].l,&line[i].r,&line[i].v);
Max=max(Max,line[i].r);
}
build(1,0,Max);
sort(line,line+n,cmp);
for(int i=0; i<n; i++)
{
_sum=0;
query(1,0,Max,line[i].l,line[i].r);
// cout<<_sum<<endl;
_sum=line[i].v-(line[i].r-line[i].l+1-_sum);
if(_sum<=0) continue;
t=_sum;
update(1,0,Max,line[i].l,line[i].r);
}
_sum=0;
query(1,0,Max,0,Max);
printf("%d\n",Max-_sum+1);
}
return 0;
}
题意:有N堵水平于坐标轴的墙,以及M只小鸟,小鸟一定会朝离自己最近的墙飞去撞晕自己,且只能飞向平行于坐标轴的 4个方向,撞到墙的边缘也算合理冲撞(即是说可以往延长线飞)。求各堵墙上各撞了几只小鸟。N, M <= 50000。
题意:给你n个数,然后有m次操作,每次操作可以删除一个数, 然后问你每次删除之前,还有多少个逆序对
标签:ems set can 差分 itemid neu 最大 集合 turn
原文地址:https://www.cnblogs.com/jaszzz/p/12945121.html