第一次学习线段树,树状数组RMQ等,努力学习,加油。
HDU 1166
题意:很清楚吧
题解:用树状数组求连续和,非常快。
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 50000+5
#define MAXN 100000+5
#define lson 2*i
#define rson 2*i+1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int c[MAX];
int a[MAX];
int sum(int x){
int ans=0;
while(x>0){
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
void add(int x,int d){
while(x<=MAX){
c[x]+=d;
x+=lowbit(x);
}
}
int main(){
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++){
int n;
scanf("%d",&n);
mem0(c);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(i,a[i]);
}
string s;
printf("Case %d:\n",t);
while(cin>>s){
int a,b;
if(s[0]=='Q'){
scanf("%d%d",&a,&b);
printf("%d\n",sum(b)-sum(a-1));
}
else if(s[0]=='A'){
scanf("%d%d",&a,&b);
add(a,b);
}
else if(s[0]=='S'){
scanf("%d%d",&a,&b);
add(a,-b);
}
else if(s[0]=='E') break;
}
}
return 0;
}
HDU 1754
题意:都明白
题解:很明显是线段树,然而我第一次撸线段树,TLE RE各种死,首先这题有点阴,给20W点,肯定会RE,看discuss里说要开三倍大才能过
然后就是建树,更新,查询,我就是更新的地方写烦了然后T了好久,更新的点应该类似于二分找到那个位置。。(单点更新)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 200000+5
#define MAXN 100000+5
#define lson 2*i
#define rson 2*i+1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int maxv[MAX*12];
int a[MAX*3];
int l,r;
int n,m;
void build(int i,int L,int R){
int mid=L+(R-L)/2;
if(L==R) maxv[i]=a[L];
else{
build(lson,L,mid);
build(rson,mid+1,R);
maxv[i]=max(maxv[lson],maxv[rson]);
}
}
void update(int i,int L,int R,int l,int r){
int mid=L+(R-L)/2;
if(L==R){
maxv[i]=r;
return;
}
if(l<=mid) update(lson,L,mid,l,r);
else update(rson,mid+1,R,l,r);
maxv[i]=max(maxv[lson],maxv[rson]);
}
int query(int i,int L,int R){
int mid=L+(R-L)/2;
int ans=0;
int a=0,b=0;
if(l<=L&&R<=r) return maxv[i];
if(l<=mid) a=query(lson,L,mid);
if(r>mid) b=query(rson,mid+1,R);
return ans=max(a,b);
}
int main(){
while(~scanf("%d%d",&n,&m)){
mem0(maxv); mem0(a);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
while(m--){
char c;
getchar();
scanf("%c%d%d",&c,&l,&r);
if(c=='Q') printf("%d\n",query(1,1,n));
else{
update(1,1,n,l,r);
}
}
}
return 0;
}
poj 3468
题解:就是线段树区间修改值,看着是个水题,模板题,然而我T啊WA 啊RE啊大半天了,不行了搞得我困死了
学习了一发完全版线段树的模板,慢慢体会了,必须靠题量和时间慢慢理解了,谁叫上学期不认真没有早点学线段树呢(区间更新)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 1000000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
LL sum[MAX<<2];
LL add[MAX<<2];
void PushUp(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m){
if(add[rt]){
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
sum[rt<<1]+=add[rt]*(m-(m>>1));
sum[rt<<1|1]+=add[rt]*(m>>1);
add[rt]=0;
}
}
void build(int l,int r,int rt){
if(l==r){
scanf("%lld",&sum[rt]);
return ;
}
int m=l+(r-l)/2;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,LL c,int l,int r,int rt){
if(L<=l&&r<=R){
add[rt]+=c;
sum[rt]+=c*(r-l+1);
}
else{
PushDown(rt,r-l+1);
int m=l+(r-l)/2;
if(L<=m) update(L,R,c,lson);
if(R>m) update(L,R,c,rson);
PushUp(rt);
}
}
LL query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R) return sum[rt];
PushDown(rt,r-l+1);
int m=l+(r-l)/2;
LL ans=0;
if(L<=m) ans+=query(L,R,lson);
if(R>m) ans+=query(L,R,rson);
return ans;
}
int main(){
int n,q;
mem0(add);
scanf("%d%d",&n,&q);
build(1,n,1);
while(q--){
char ch;
int a,b;
LL c;
getchar();
scanf("%c",&ch);
if(ch=='Q'){
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
else{
scanf("%d%d%lld",&a,&b,&c);
update(a,b,c,1,n,1);
}
}
return 0;
}
poj 2528
题意:给你n个海报,按照顺序贴墙上,问你最后能看到多少个(未被覆盖的有多少个)
题解:区间的维护,用线段树(虽然本渣一开始完全想不到线段树怎么弄啊)
这题数据有点大,所以需要离散化(排序去重
但是只是这样简单的离散化是错误的,
如三张海报为:1~10 1~4 6~10
离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。
新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)
X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10
这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3
最终,1~2为2,3为1,4~5为3,于是输出正确结果3。
(然而poj这么写了会WA,不这么离散反而能AC,不造为啥,反正知道这个方法是对的就行了)
线段树还得多刷题
<span style="font-size:12px;">#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 20000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int l[MAX],r[MAX];
int x[MAX<<3];
int root[MAX<<4];
int vis[MAX<<2];
int cnt;
void PushDown(int rt){
root[rt<<1]=root[rt<<1|1]=root[rt];
root[rt]=-1;
}
void update(int l,int r,int rt,int c,int L,int R){
if(L<=l&&r<=R){
root[rt]=c;
return;
}
if(root[rt]!=-1) PushDown(rt);
int m=l+(r-l)/2;
if(L<=m) update(lson,c,L,R);
if(R>m) update(rson,c,L,R);
}
int Bsearch(int a,int b,int xx){
int l=a,r=b;
while(l<=r){
int mid=(l+r)/2;
if(x[mid]<xx) l=mid+1;
else r=mid-1;
}
return l;
}
void query(int l,int r,int rt){
if(l==r){
if(!vis[root[rt]]){
cnt++;
vis[root[rt]]=1;
}
return;
}
if(root[rt]!=-1) PushDown(rt);
int m=l+(r-l)/2;
query(lson);
query(rson);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
int nn=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&l[i],&r[i]);
x[++nn]=l[i];
x[++nn]=r[i];
}
sort(x+1,x+nn+1);
int mm=1;
for(int i=2;i<=nn;i++){
if(x[i]!=x[i-1]) x[++mm]=x[i];
}
mem1(root);
mem0(vis);
for(int i=1;i<=n;i++){
int L=Bsearch(1,mm,l[i]);
int R=Bsearch(1,mm,r[i]);
update(1,mm,1,i,L,R);
}
cnt=0;
query(1,mm,1);
printf("%d\n",cnt);
}
return 0;
}
</span>
hdu 1698
题意:屠夫的钩子有n段,每段的值为1,现在屠夫可以在一段区间内把钩子的值变成1,2,3;
题解:显然是线段树,区间替换,这题只需要更新,然后询问rt=1的点,所以没有写query函数,感觉还是比较清楚的(第一次写区间更换,感觉还得多刷点模板题,模板都没写熟练呢,哎)还有数组每次开4倍,*4总是RE,写成<<2就能过了,什么仇什么怨
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 100000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int sum[MAX<<2];
int col[MAX<<2];
void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int rt,int m){
if(col[rt]){
col[rt<<1]=col[rt<<1|1]=col[rt];
sum[rt<<1]=col[rt]*(m-(m>>1));
sum[rt<<1|1]=col[rt]*(m>>1);
col[rt]=0;
}
}
void build(int l,int r,int rt){
if(l==r){
sum[rt]=1;
return;
}
int m=l+(r-l)/2;
build(lson);
build(rson);
pushup(rt);
}
void update(int l,int r,int rt,int L,int R,int c){
if(L<=l&&r<=R){
col[rt]=c;
sum[rt]=c*(r-l+1);
return;
}
pushdown(rt,r-l+1);
int m=l+(r-l)/2;
if(L<=m) update(lson,L,R,c);
if(R>m) update(rson,L,R,c);
pushup(rt);
}
int main(){
int t;
scanf("%d",&t);
int kase=0;
while(t--){
int n,q;
scanf("%d%d",&n,&q);
mem0(col);
build(1,n,1);
while(q--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
update(1,n,1,a,b,c);
}
printf("Case %d: The total value of the hook is %d.\n",++kase,sum[1]);
}
return 0;
}
zoj 1610
题意:给你一段区间,从0-8000(这是个坑点,一开始以为是0-n,跪了会),然后给你n次涂色,每次可以覆盖以前的,问你最后墙上每种颜色出现了多少段(如果同种颜色中间被其他颜色或者没有颜色分开了,那么就是两段)
题解:线段树区间维护啊,然后给你的是点,让你涂色的两点之间的区间,所以把输入的左端点+1,把区间压缩成点,这样容易想一点,就是每次涂色的是一段点,然后求多少段。
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 8000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int col[MAX<<2];
int vis[MAX<<2];
int ans[MAX<<2];
int colour[MAX<<2];
void pushdown(int rt){
if(col[rt]!=-1){
col[rt<<1]=col[rt<<1|1]=col[rt];
col[rt]=-1;
}
}
void update(int l,int r,int rt,int L,int R,int c){
if(L<=l&&r<=R){
col[rt]=c;
return;
}
pushdown(rt);
int m=l+(r-l)/2;
if(L<=m) update(lson,L,R,c);
if(R>m) update(rson,L,R,c);
}
void query(int l,int r,int rt){
if(l==r){
if(col[rt]!=-1){
ans[l]=col[rt];
}
return;
}
pushdown(rt);
int m=l+(r-l)/2;
query(lson);
query(rson);
}
int main(){
int n;
while(~scanf("%d",&n)){
mem1(col);
mem0(vis);
mem1(ans);
mem0(colour);
for(int i=0;i<n;i++) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
update(1,8000,1,a+1,b,c);
}
query(1,8000,1);
for(int i=1;i<=8000;i++){
if(ans[i]!=-1){
if(i==1) colour[ans[i]]++;
else if(ans[i]!=ans[i-1]) colour[ans[i]]++;
}
}
for(int i=0;i<=8000;i++){
if(colour[i]) printf("%d %d\n",i,colour[i]);
}
printf("\n");
}
return 0;
}
poj 3264
题意:给你n个牛,求区间内最高的牛和最低的牛的差
题解:线段树维护区间内的最大最小值啊怒1A
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 50000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int a[MAX];
int maxv[MAX<<2];
int minv[MAX<<2];
int _max,_min;
void pushup(int rt){
maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l==r){
scanf("%d",&maxv[rt]);
minv[rt]=maxv[rt];
return;
}
int m=l+(r-l)/2;
build(lson);
build(rson);
pushup(rt);
}
void query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R){
_max=max(_max,maxv[rt]);
_min=min(_min,minv[rt]);
return ;
}
int m=l+(r-l)/2;
if(L<=m) query(lson,L,R);
if(R>m) query(rson,L,R);
}
int main(){
int n,q;
scanf("%d%d",&n,&q);
build(1,n,1);
while(q--){
int a,b;
scanf("%d%d",&a,&b);
_max=0;
_min=INF;
query(1,n,1,a,b);
printf("%d\n",_max-_min);
}
return 0;
}
hdu 4027
题意:给你n个数,然后q次询问,t=0的时候把区间内的值都开根取整,t=1的时候就输出区间的和
题解:
刚开始的时候第一反应这道题是成段的更新,不能一个数一个数的更新,那样肯定会超时的!
但是再想了一会儿之后,发现成段更新比较困难,主要是这道题的每个节点的更新并不是统一的(也就是说并不是都加K,或者都减K之类的);
所以我们并不能像一般的成段更新那样去更新。
再仔细观察后,其实我们可以很容易的发现,一个数k(k<=2^63-1)在经过最多6,7次的开平方根后,必然会变成1,而且当1的平方根也是1;
也就是说当一个数为1的时候,我们没有必要对它进行操作和更新;而且一个很大的数仅仅经过6,7次就可以变成1;
所以到这里我们因该就可以形成一个解题的大体思路了:
每当我们要进行更新操作的时候,我们先判断一下这个区间是否有必要进行更新(若全都是1就没有更新的必要了);
判断的方法很简单:就是看该区间的长度和该区间内的总值是否相等;
然后要不停的更新
接下来的就是很水很水的区间求和了!!!!!!!!!!(给的区间X,Y有可能是X>Y,坑爹啊)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 100000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
LL sum[MAX<<2];
LL ans;
void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
scanf("%I64d",&sum[rt]);
return;
}
int m=l+(r-l)/2;
build(lson);
build(rson);
pushup(rt);
}
void update(int l,int r,int rt,int L,int R){
if(sum[rt]==(LL)(r-l+1)) return;
if(l==r){
sum[rt]=sqrt(double(sum[rt]));
return;
}
int m=l+(r-l)/2;
if(L<=m) update(lson,L,R);
if(R>m) update(rson,L,R);
pushup(rt);
}
void query(int l,int r,int rt,int L, int R){
if(L<=l&&r<=R){
ans+=sum[rt];
return;
}
int m=l+(r-l)/2;
if(L<=m) query(lson,L,R);
if(R>m) query(rson,L,R);
}
int main(){
int n;
int kase=0;
while(~scanf("%d",&n)){
kase++;
printf("Case #%d:\n",kase);
build(1,n,1);
int q;
scanf("%d",&q);
while(q--){
int t,a,b;
scanf("%d%d%d",&t,&a,&b);
if(a>b) swap(a,b);
if(t==0){
update(1,n,1,a,b);
}
else{
ans=0;
query(1,n,1,a,b);
printf("%I64d\n",ans);
}
}
printf("\n");
}
return 0;
}
hdu 1394
题意:给你n个数字,然后这个序列可循环(就是可以每次把头上一个数字放到序列的最后),且这个序列的数字不重复,从0-n-1
然后问你这样的n个序列中逆序对数最小是多少
题解:我觉得这题好难啊,为啥过的人那么多而且AC率这么高。可能我太弱了吧,刚接触线段树
然后这题的思路是先找到输入的序列的逆序对数,然后头上一个数字放到最后,如果头上的数字是a[i],那么放到后面,逆序对数量的变化是-a[i]+n-1-a[i],所以只需要求出输入的数的逆序对数(貌似方法很多吧(分治+归并排序),线段树这个方法过于精妙)
线段树的方法是因为这些数字大小是0-n-1,然后输入一个数字的时候,逆序对数就是前面已经输入的比他大的数字的个数,所以可以每输入一个数字a,用它的大小对应的区间做出修改,就是等同于单点修改,在a点+1,然后更新区间,每次输入一个数字b的时候,就是求b-n-1这个区间里的和(这个区间里出现过的数组那个点就会+1)
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 10000+5
#define MAXN 100000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define LL long long
#define ull unsigned long long
#define mem0(x) memset(x,0,sizeof(x))
#define mem1(x) memset(x,-1,sizeof(x))
#define meminf(x) memset(x,INF,sizeof(x))
#define lowbit(x) (x&-x)
const int mod = 1000000007;
const int prime = 999983;
const int INF = 0x3f3f3f3f;
const int INFF = 1e9;
const double pi = 3.141592653589793;
const double inf = 1e18;
const double eps = 1e-10;
//读入外挂
inline int read_int(){
int ret=0;
char tmp;
while(!isdigit(tmp=getchar()));
do{
ret=(ret<<3)+(ret<<1)+tmp-'0';
}while(isdigit(tmp=getchar()));
return ret;
}
int a[MAX];
int sum[MAX<<2];
void pushup(int rt){
sum[rt]=sum[lrt]+sum[rrt];
}
void update(int p,int l,int r,int rt){
if(l==r){
sum[rt]++;
return;
}
int m=l+(r-l)/2;
if(p<=m) update(p,lson);
else update(p,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R) return sum[rt];
int m=l+(r-l)/2;
int ans=0;
if(L<=m) ans+=query(L,R,lson);
if(R>m) ans+=query(L,R,rson);
return ans;
}
int main(){
int n;
while(~scanf("%d",&n)){
int cnt=0;
mem0(sum);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
cnt+=query(a[i],n-1,0,n-1,1);
update(a[i],0,n-1,1);
}
int mini=INF;
for(int i=0;i<n;i++){
cnt+=(n-1-2*a[i]);
mini=min(mini,cnt);
}
printf("%d\n",mini);
}
return 0;
}
hdu 2795
题意:给你一个h*w的木板,然后上面可以贴海报,海报是1*L的,然后每次都贴最上面的最左边的空位,问你每个海报贴在第几行
题解:我一开始都看不出是线段树啊,太弱了,h可以是10^9,然后吓住了,看了notonlysuccess写的blog,想想也是啊,每行都是一样宽,如果n行放不下,那么h行也放不下,所以h完全可以变成n,然后就是20W的数据量,就是维护每行的最大值
int maxv[MAX<<2];
int h,w,n;
int cnt;
void pushup(int rt){
maxv[rt]=max(maxv[lrt],maxv[rrt]);
}
void build(int l,int r,int rt){
if(l==r){
maxv[rt]=w;
return;
}
int m=l+(r-l)/2;
build(lson);
build(rson);
pushup(rt);
}
void query(int a,int l,int r,int rt){
if(maxv[rt]<a) return;
if(cnt!=-1) return;
if(l==r){
maxv[rt]-=a;
cnt=l;
return;
}
int m=l+(r-l)/2;
query(a,lson);
query(a,rson);
pushup(rt);
}
int main(){
while(~scanf("%d%d%d",&h,&w,&n)){
if(h>n) h=n;
build(1,n,1);
while(n--){
int a;
scanf("%d",&a);
cnt=-1;
query(a,1,h,1);
printf("%d\n",cnt);
}
}
return 0;
}
poj 2828
题意:很多人插队,每个人有一个val,然后插在第几个人后面,最后输出这一列人的每个人的val
题解:这题难点在于想,我也是看了题解(哎虽然刷的是线段树专题,但是很多题完全想不到线段树啊,看来还是脑洞不行)
这题就是先输入,然后倒着更新,因为最后一个人的位置是确定的(pos+1),然后这样想,更新前面前面插队的人的时候,在他后面插队的对他没有影响,然后把后面插队的(已经插入线段树中的)去掉,在这些空位里,这个人也是在pos+1的位置上,然后开个sum数组记录这个区间内的空位,然后更新位置时候,如果这个位置大于左儿子的空位,那么就插入右儿子(需要减去左儿子的空位数)
int pos[MAX],val[MAX];
int sum[MAX<<2];
int col[MAX<<2];
int n;
void pushup(int rt){
sum[rt]=sum[lrt]+sum[rrt];
}
void build(int l,int r,int rt){
if(l==r){
sum[rt]=1;
return ;
}
mid;
build(lson);
build(rson);
pushup(rt);
}
void update(int p,int v,int l,int r,int rt){
if(l==r){
sum[rt]=0;
col[rt]=v;
return;
}
mid;
if(p<=sum[lrt]) update(p,v,lson);
else update(p-sum[lrt],v,rson);
pushup(rt);
}
void query(int l,int r,int rt){
if(l==r){
printf("%d",col[rt]);
if(l!=n) printf(" ");
else printf("\n");
return;
}
mid;
query(lson);
query(rson);
}
int main(){
while(~scanf("%d",&n)){
for(int i=0;i<n;i++) scanf("%d%d",&pos[i],&val[i]);
build(1,n,1);
for(int i=n-1;i>=0;i--) update(pos[i]+1,val[i],1,n,1);
query(1,n,1);
}
return 0;
}
poj 3667
题意:给你n个房间,然后一开始都是空的,然后有长度为d的队伍要住进去,输出开始房间序号(要最小),不存在就输出0,还可以把一段房间内住的人都清空。
题解:显然是线段树了,如何判断一个区间内最长的连续的空房间是否大于a,需要参数sum(区间内最大连续个数),lsum(从最左端开始的最长连续个数),rsum(从右端开始的最长连续个数)
因为一个区间的最大连续个数sum[rt]=max(rsum[lrt]+lsum[rrt],max(sum[lrt],sum[rrt]));
然后还需要一个cover数组来记录当前区间是否被覆盖(住人)
这种需要前缀后缀的,求连续的线段树我还是不行啊,还得多练习,不能只会做万人轮的水题
int cover[MAX<<2],sum[MAX<<2],lsum[MAX<<2],rsum[MAX<<2];
void pushdown(int rt,int m){
if(cover[rt]!=-1){
cover[lrt]=cover[rrt]=cover[rt];
if(cover[rt]==0){
lsum[lrt]=rsum[lrt]=sum[lrt]=m-(m>>1);
lsum[rrt]=rsum[rrt]=sum[rrt]=m>>1;
}
else{
lsum[lrt]=rsum[lrt]=sum[lrt]=lsum[rrt]=rsum[rrt]=sum[rrt]=0;
}
cover[rt]=-1;
}
}
void pushup(int rt,int m){
lsum[rt]=lsum[lrt];
rsum[rt]=rsum[rrt];
if(lsum[rt]==m-(m>>1)) lsum[rt]+=lsum[rrt];
if(rsum[rt]==m>>1) rsum[rt]+=rsum[lrt];
sum[rt]=max(rsum[lrt]+lsum[rrt],max(sum[lrt],sum[rrt]));
}
void build(int l,int r,int rt){
cover[rt]=-1;
sum[rt]=lsum[rt]=rsum[rt]=r-l+1;
if(l==r) return;
mid;
build(lson);
build(rson);
}
void update(int L,int R,int c,int l,int r,int rt){
if(L<=l&&r<=R){
cover[rt]=c;
if(cover[rt]==0) sum[rt]=lsum[rt]=rsum[rt]=r-l+1;
else sum[rt]=lsum[rt]=rsum[rt]=0;
return;
}
pushdown(rt,r-l+1);
mid;
if(L<=m) update(L,R,c,lson);
if(R>m) update(L,R,c,rson);
pushup(rt,r-l+1);
}
int query(int a,int l,int r,int rt){
if(l==r) return 1;
pushdown(rt,r-l+1);
mid;
if(sum[lrt]>=a) return query(a,lson);
else if(rsum[lrt]+lsum[rrt]>=a) return m-rsum[lrt]+1;
else query(a,rson);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
build(1,n,1);
mem1(cover);
while(m--){
int op;
scanf("%d",&op);
if(op==1){
int a;
scanf("%d",&a);
if(sum[1]<a) printf("0\n");
else{
int pos=query(a,1,n,1);
printf("%d\n",pos);
update(pos,pos+a-1,1,1,n,1);
}
}
else{
int a,b;
scanf("%d%d",&a,&b);
update(a,a+b-1,0,1,n,1);
}
}
return 0;
}