HDU2665
题意:给你一个数列长为n,(1<=n<=100000),q次询问,(1<=q<=100000)
题解:高级数据结构
hdu2665
+ 尝试一
数列分块(求小于某个数的个数),再加二分得到答案。
结果(TLE),时间复杂度(
)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
ll read()
{
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxn=50005;
int n,block;
int v[maxn],bl[maxn],atag[maxn];
vector<int >ve[maxn];
void add(int a,int b,int c)
{
for(int i=a; i<=min(b,block*bl[a]); i++)
{
v[i]+=c;
}
if(bl[a]!=bl[b])
{
for(int i=bl[b]*(block-1)+1; i<=b; i++)
{
v[i]+=c;
}
}
for(int i=bl[a]+1; i<=bl[b]-1; i++)
{
atag[i]+=c;
}
}
int query(int a,int b,int x)
{
int ans=0;
for(int i=1; i<=min(bl[a]*block,b); i++)
{
if(v[i]+atag[bl[a]]<x)
{
ans++;
}
}
if(bl[a]!=bl[b])
for(int i=block*(bl[b]-1)+1; i<=b; i++)
{
if(v[i]+atag[bl[b]]<x)
ans++;
}
for(int i=bl[a]+1; i<=bl[b]-1; i++)
{
x-=atag[i];
ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin();
}
return ans;
}
int main()
{
int f,a,b,c,k,q;
int t;
scanf("%d",&t);
while(t--)
{
for(int i=0;i<1000;i++)ve[i].clear();
n=read();
q=read();
block=sqrt(n);
mem(atag,0);
for(int i=1; i<=n; i++)
{
v[i]=read();
bl[i]=(i-1)/block+1;
}
for(int i=1; i<=n; i++)
{
ve[bl[i]].push_back(v[i]);
}
for(int i=1; i<=bl[n]; i++)
{
sort(ve[i].begin(),ve[i].end());
}
for(int i=1; i<=q; i++)
{
a=read();
b=read();
k=read();
int l=1,r=1e5;
while(l<=r)
{
int m=(l+r)/2;
if(query(a,b,m)<=(k-1))
l=m+1;
else
r=m-1;
}
printf("%d\n",r);
}
}
return 0;
}
- 尝试二
贴一个单次树状数组求第k大代码;(感觉还不如快排>_<)
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int bit[maxn],v[maxn];
int n,m,q;
vector<int>hash;
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
for(int i=x; i<=m; i+=lowbit(i))
{
bit[i]+=1;
}
}
int ask(int x)
{
int sum=0;
for(int i=x; i>0; i-=lowbit(i))
{
// cout<<"??"<<' '<<i<<endl;
sum+=bit[i];
}
return sum;
}
int main()
{
int k;
while(scanf("%d%d",&n,&k)!=EOF)
{
hash.clear();
memset(bit,0,sizeof bit);
for(int i=1; i<=n; i++)
{
scanf("%d",&v[i]);
hash.push_back(v[i]);
}
sort(hash.begin(),hash.end());
hash.erase(unique(hash.begin(),hash.end()),hash.end());
m=hash.size();
for(int i=1; i<=n; i++)
{
int d=lower_bound(hash.begin(),hash.end(),v[i])-hash.begin();
add(d+1);
}
int l=1,r=m;
while(l<=r)
{
int mid=(l+r)/2;
if(ask(mid)<=k)
l=mid+1;
else
r=mid-1;
}
if(ask(r)<k)r++;
printf("%d\n",hash[r-1]);
}
return 0;
}
- 尝试三
堆排,用一个堆存前k大的k个元素,最后pop(); - 尝试四
模拟快排->
单次查询复杂度接近 但是区间动态后 ,无力求解
非TLE的解法看下面
+ 尝试一
主席树:将数列的每个前缀建一棵线段树,技巧通过观察可以发现第i颗树和第i+1颗树只有一条链不同,所以可以在原来的基础上建树(避免MLE);树的叶子节点存的是小于当前节点下标的值的个数。所以求区间
既是求
。
贴一个学习主席树的博客->
#include<bits/stdc++.h>
#define lson l,m
#define rson m+1,r
using namespace std;
const int maxn=100005;
struct Node{
int l,r,sum;
}p[maxn<<5];//毕竟有100000颗线段树。
int T[maxn],a[maxn],Hash[maxn],cnt=0;
int build(int l,int r)
{
int rt=(++cnt);
if(l==r)
{
return p[rt].sum=0;
}
int m=(l+r)>>1;
p[rt].l=build(lson);//注意这里的左右子树不在通过计算得到
p[rt].r=build(rson);//因为多棵线段树的左右节点掺杂,不存在原来线段树的计算规律。
return rt;
}
int update(int pre,int l,int r,int x)
{
int rt=(++cnt);//rt为新加的一条树链的节点
p[rt].sum=p[pre].sum+1;
p[rt].l=p[pre].l;
p[rt].r=p[pre].r;
if(l==r)return rt;
int m=(l+r)>>1;
if(x<=m)p[rt].l=update(p[pre].l,lson,x);
else p[rt].r=update(p[pre].r,rson,x);
return rt;
}
int query(int u,int v,int l,int r,int k)
{
if(l==r)
{
return l;
}
int m=(l+r)>>1;
int num=p[p[v].l].sum-p[p[u].l].sum;
if(num>=k)return query(p[u].l,p[v].l,lson,k);
else return query(p[u].r,p[v].r,rson,k-num);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
cnt=0;
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
Hash[i]=a[i];
}
sort(Hash+1,Hash+1+n);
int d=unique(Hash+1,Hash+n+1)-Hash-1;
T[0]=build(1,d);
for(int i=1;i<=n;i++)
{
int x=lower_bound(Hash+1,Hash+d+1,a[i])-Hash;
T[i]=update(T[i-1],1,d,x);
}
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int x=query(T[l-1],T[r],1,d,k);
printf("%d\n",Hash[x]);
}
}
return 0;
}
//大佬的不用结构体的模板
#include<bits/stdc++.h>
#define lson l, m
#define rson m+1, r
using namespace std;
const int N=1e5+5;
int L[N<<5], R[N<<5], sum[N<<5];
int tot;
int a[N], T[N], Hash[N];
int build(int l, int r)
{
int rt=(++tot);
sum[rt]=0;
if(l<r)
{
int m=(l+r)>>1;
L[rt]=build(lson);
R[rt]=build(rson);
}
return rt;
}
int update(int pre, int l, int r, int x)
{
int rt=(++tot);
L[rt]=L[pre], R[rt]=R[pre], sum[rt]=sum[pre]+1;
if(l<r)
{
int m=(l+r)>>1;
if(x<=m)
L[rt]=update(L[pre], lson, x);
else
R[rt]=update(R[pre], rson, x);
}
return rt;
}
int query(int u, int v, int l, int r, int k)
{
if(l>=r)
return l;
int m=(l+r)>>1;
int num=sum[L[v]]-sum[L[u]];
if(num>=k)
return query(L[u], L[v], lson, k);
else
return query(R[u], R[v], rson, k-num);
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
tot=0;
int n, m;
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
Hash[i]=a[i];
}
sort(Hash+1, Hash+n+1);
int d=unique(Hash+1, Hash+n+1)-Hash-1;
T[0]=build(1, d);
for(int i=1; i<=n; i++)
{
int x=lower_bound(Hash+1, Hash+d+1, a[i])-Hash;
T[i]=update(T[i-1], 1, d, x);
}
while(m--)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int x=query(T[l-1], T[r], 1, d, k);
printf("%d\n", Hash[x]);
}
}
}
poj2104
由于q较小且时间较多,数列分块能过的题
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,blo;
int v[500005],bl[500005],atag[500005],tp[100005];
vector<int>ve[505];
//void reset(int x)
//{
// ve[x].clear();
// for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
// ve[x].push_back(v[i]);
// sort(ve[x].begin(),ve[x].end());
//}
void add(int a,int b,int c)
{
for(int i=a;i<=min(bl[a]*blo,b);i++)
v[i]+=c;
// reset(bl[a]);
if(bl[a]!=bl[b])
{
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
v[i]+=c;
// reset(bl[b]);
}
for(int i=bl[a]+1;i<=bl[b]-1;i++)
atag[i]+=c;
}
int query(int a,int b,int c)
{
int ans=0;
for(int i=a;i<=min(bl[a]*blo,b);i++)
if(v[i]+atag[bl[a]]<c)ans++;
if(bl[a]!=bl[b])
for(int i=(bl[b]-1)*blo+1;i<=b;i++)
if(v[i]+atag[bl[b]]<c)ans++;
// cout<<"ans: "<<ans<<endl;
for(int i=bl[a]+1;i<=bl[b]-1;i++)
{
int x=c-atag[i];
ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin();
}
return ans;
}
int main()
{
int f,a,b,c,k,q;
int t;
// while(t--)
{
for(int i=0;i<105;i++)ve[i].clear();
n=read();
q=read();
blo=1000;//这里注意block的设置,如果设为sqrt(n)会超时
mem(atag,0);
for(int i=1; i<=n; i++)
{
v[i]=read();
tp[i]=v[i];
bl[i]=(i-1)/blo+1;
}
for(int i=1; i<=n; i++)
{
ve[bl[i]].push_back(v[i]);
}
for(int i=1; i<=bl[n]; i++)
{
sort(ve[i].begin(),ve[i].end());
}
sort(tp+1,tp+n+1);
for(int i=1; i<=q; i++)
{
a=read();
b=read();
k=read();
int l=1,r=n;
while(l<=r)
{
int m=(l+r)/2;
if(query(a,b,tp[m])<=(k-1))
l=m+1;
else
r=m-1;
}
printf("%d\n",tp[r]);
}
}
return 0;
}
/*
6778
5 3
3 1 2 4 5
*/
主席树同样能过
#include<cstdio>
#include<algorithm>
#define lson l,m
#define rson m+1,r
using namespace std;
const int maxn=100005;
struct Node{
int l,r,sum;
}p[maxn<<5];//毕竟有100000颗线段树。
int T[maxn],a[maxn],Hash[maxn],cnt=0;
int build(int l,int r)
{
int rt=(++cnt);
if(l==r)
{
return p[rt].sum=0;
}
int m=(l+r)>>1;
p[rt].l=build(lson);//注意这里的左右子树不在通过计算得到
p[rt].r=build(rson);//因为多棵线段树的左右节点掺杂,不存在原来线段树的计算规律。
return rt;
}
int update(int pre,int l,int r,int x)
{
int rt=(++cnt);//rt为新加的一条树链的节点
p[rt].sum=p[pre].sum+1;
p[rt].l=p[pre].l;
p[rt].r=p[pre].r;
if(l==r)return rt;
int m=(l+r)>>1;
if(x<=m)p[rt].l=update(p[pre].l,lson,x);
else p[rt].r=update(p[pre].r,rson,x);
return rt;
}
int query(int u,int v,int l,int r,int k)
{
if(l==r)
{
return l;
}
int m=(l+r)>>1;
int num=p[p[v].l].sum-p[p[u].l].sum;
if(num>=k)return query(p[u].l,p[v].l,lson,k);
else return query(p[u].r,p[v].r,rson,k-num);
}
int main()
{
int t;
// scanf("%d",&t);
// while(t--)
{
cnt=0;
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
Hash[i]=a[i];
}
sort(Hash+1,Hash+1+n);
int d=unique(Hash+1,Hash+n+1)-Hash-1;
T[0]=build(1,d);
for(int i=1;i<=n;i++)
{
int x=lower_bound(Hash+1,Hash+d+1,a[i])-Hash;
T[i]=update(T[i-1],1,d,x);
}
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int x=query(T[l-1],T[r],1,d,k);
printf("%d\n",Hash[x]);
}
}
return 0;
}
神奇的莫队+树状数组(同样是分块,莫队的时间复杂度很客观,棒)
贴一个博客->
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
// head
const int N = 1e5+5;
const int M = 5e3+5;
const int B = 1000;
struct BIT {
#define T int
T tree[N] ;
inline int lowbit(int x) {
return x&(-x);
}
void add(int x, T add, int n) {
for (int i = x; i <= n; i += lowbit(i)) {
tree[i] += add;
}
}
T sum(int x) {
T ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
ans += tree[i];
}
return ans;
}
T query(int k, int n) {
int l = 1, r = n, ans = n;
while (l <= r) {
int mid = (l + r) / 2;
if (sum(mid) >= k) {
ans = mid;
r = mid-1;
} else {
l = mid+1;
}
}
return ans;
}
void clear(int n) {
for (int i = 1; i <= n; i++) {
tree[i] = 0;
}
}
#undef T
};
BIT bt;
struct Query {
int l, r, k, id;
};
bool cmp(const Query &a, const Query &b) {
int apos = a.l / B, bpos = b.l / B;
return apos == bpos ? a.r < b.r : apos < bpos;
}
Query query[M];
int a[N];
int dis[N];
int res[M];
void add(int x, int n) {
bt.add(x, 1, n);
}
void rem(int x, int n) {
bt.add(x, -1, n);
}
int main() {
int n, m;
while (scanf("%d%d", &n, &m) == 2) {
for (int i = 1; i <= n; i++) {
scanf("%d", a+i);
dis[i-1] = a[i];
}
sort(dis, dis + n);
int tot = unique(dis, dis + n) - dis;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(dis, dis + tot, a[i]) - dis + 1;
}
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &query[i].l, &query[i].r, &query[i].k);
query[i].id = i;
}
sort(query, query + m, cmp);
int l = 1, r = 0;
for (int i = 0; i < m; i++) {
while (r < query[i].r) add(a[++r], tot);
while (l > query[i].l) add(a[--l], tot);
while (r > query[i].r) rem(a[r--], tot);
while (l < query[i].l) rem(a[l++], tot);
res[query[i].id] = bt.query(query[i].k, tot);
}
for (int i = 0; i < m; i++) {
printf("%d\n", dis[res[i]-1]);
}
bt.clear(tot);
}
return 0;
}