特殊做法类线段树

有一类神奇的线段树,我们来总结一下。。。。
- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COGS  1272 AHOI  行星序列
一道看起来很简单的线段树,有两种操作,包括*k,+a,但经过思考我们发现,但操作顺序不同时(先乘后加还是先加后乘),所以我们统一为一种操作(*k => *k+0  +a => *1+a) 于是水过。。。code:
#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
long long node[400001],a[100001];
int n,p,m;
long long  delta[400001][2],ans;
void updata(int i)
{
	node[i]=(node[i<<1]+node[i<<1|1])%p;
}
void build(int i,int l,int r)
{
	delta[i][0]=1;
	delta[i][1]=0;
	if (l==r)
	  {
	    node[i]=a[l]%p;
	    return;
	  }
	build(lch); build(rch);
	updata(i);
}
void paint(int i,int l,int r,long long k,long long a)
{
	int t;
	a=a%p;  k=k%p; t=(r-l+1)%p; 
	node[i]=((node[i]*k)%p+(a*t)%p)%p;
	delta[i][0]=(delta[i][0]*k)%p;
	delta[i][1]=((delta[i][1]*k)%p+a)%p;
}
void pushdown(int i,int l,int r)
{
	paint(lch,delta[i][0],delta[i][1]);
	paint(rch,delta[i][0],delta[i][1]);
	delta[i][0]=1; delta[i][1]=0;
}
void insert(int i,int l,int r,int x,int y,long long a,int kind)
{
	if (x<=l&&y>=r)
	  {
	  	if (kind==2)
	      paint(i,l,r,1,a);
	    if (kind==1)
	      paint(i,l,r,a,0);
	    return;
	  }
	pushdown(i,l,r);
	if (x<=mid) insert(lch,x,y,a,kind);
	if (y>mid) insert(rch,x,y,a,kind);
	updata(i);
}
void query(int i,int l,int r,int x,int y)
{
	if (x<=l&&y>=r)
	  {
	    ans=(node[i]+ans)%p;
		return;   
	  }
	pushdown(i,l,r);
	if (x<=mid) query(lch,x,y);
	if (y>mid) query(rch,x,y);
}
int main()
{
	freopen("seqb.in","r",stdin);
	freopen("seqb.out","w",stdout);
	int i,kind,t,g,c;
	scanf("%d%d",&n,&p);
	for (i=1;i<=n;++i)
	 scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	for (i=1;i<=m;++i)
	  {
	    scanf("%d",&kind);
	    if (kind==1||kind==2)
	      {
	      	c=c%p;
	        scanf("%d%d%d",&t,&g,&c);
			insert(1,1,n,t,g,c,kind); 
	      }
	    if (kind==3)
	      {
	        scanf("%d%d",&t,&g);
	        ans=0; query(1,1,n,t,g);
	        printf("%lld\n",ans);
	      }
	  }
	fclose(stdin);
	fclose(stdout);
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COGS  三元数对&&数列
      统计在某数之前比某数小和某数之后比某数小(或大)的个数,思路很奇特,以数字为节点建树,然后在线单点修改,区间求值。。。
 三元数对(离散化)code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
int node[200000],n;
int a[30001],b[30001];
int minn[30001];
int ans;
long long sum;
void updata(int i)
{
	node[i]=node[i<<1]+node[i<<1|1];
}
void insert(int i,int l,int r,int x)
{
	if (l==r&&l==x)
	  {
	    node[i]+=1;
	    return;
	  }
	if (x<=mid) insert(lch,x);
	else insert(rch,x);
	updata(i);
}
void query(int i,int l,int r,int x,int y)
{
	if (x<=l&&y>=r)
	  {
	    ans+=node[i];
		return;     
	  }
	if (x<=mid) query(lch,x,y);
	if (y>mid) query(rch,x,y);
}
int main()
{
	int i,size;
	freopen("three.in","r",stdin);
	freopen("three.out","w",stdout);
	scanf("%d",&n);
	sum=0;
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&a[i]);
	    a[i]+=2;
	    b[i]=a[i];
	  }
	sort(b+1,b+n+1);
	size=unique(b+1,b+n+1)-b-1;
	for (i=1;i<=n;++i)
	    a[i]=upper_bound(b+1,b+size+1,a[i])-b;
	size++;
	memset(node,0,sizeof(node));
	for (i=1;i<=n;++i)
	  {
	    insert(1,1,size,a[i]);
	    if (i>1)
	      {
	        ans=0;
	        query(1,1,size,1,a[i]-1);
	        minn[i]=ans;
	      }
	  }
	memset(node,0,sizeof(node));
	for (i=n;i>=1;--i)
	  {
	    insert(1,1,size,a[i]);
	    if (i<n)
	      {
	        ans=0;
	        query(1,1,size,a[i]+1,size);
	        if (i>1)
	          sum+=ans*minn[i];
	      }
	  }
	printf("%lld\n",sum);
	fclose(stdin);
	fclose(stdout);
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TYVJ 1728 普通平衡树
一道著名的平衡树练习题,我们考虑它的线段树做法(做死了。。。),首先离散化(为了输出原数搞一个类似映射的东西),然后以数为节点建立线段树,然后blabla乱搞就好了(蒟蒻的算法常数很大,很大) code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
#define lch i<<1,l,mid
#define rch i<<1|1,mid+1,r
using namespace std;
int node[400001],n;
struct hp{
	int kind,x;
}opt[100001];
int b[100001];
int f[100001],ans,rank,ansi;
void updata(int i)
{
	node[i]=node[i<<1]+node[i<<1|1];
}
void insert(int i,int l,int r,int x,int a)
{
	if (l==r&&l==x)
	  {
	    node[i]+=a;
	    return;
	  }
	if (x<=mid) insert(lch,x,a);
	else insert(rch,x,a);
	updata(i);
}
void querynum(int i,int l,int r,int x)
{
	if (l==r&&l==x)
	  {ansi=i; ans++; return;}
	if (x<=mid)
	  querynum(lch,x);
	else
	  {
	  	ans+=node[i<<1];
	    querynum(rch,x);
	  }
}
void queryx(int i,int l,int r,int x)
{
	if (l==r&&rank<=x&&rank+node[i]>=x)
	  {
	    ans=l;
		return; 
	  } 
	if (rank+node[i<<1]>=x)
	  queryx(lch,x);
	else
	  {
	    rank+=node[i<<1];
		queryx(rch,x); 
	  } 
}
int main()
{
	int t,i,size;
	freopen("phs.in","r",stdin);
	freopen("phs.out","w",stdout);
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	  {
	    scanf("%d%d",&opt[i].kind,&opt[i].x);
	    b[i]=opt[i].x;
	  }
	sort(b+1,b+n+1);
	size=unique(b+1,b+n+1)-b-1;
	for (i=1;i<=n;++i)
	  {
	    t=lower_bound(b+1,b+size+1,opt[i].x)-b;
		f[t]=opt[i].x;
		opt[i].x=t;  
	  }
	for (i=1;i<=n;++i)
	  {
        if (opt[i].kind==1)
          insert(1,1,size,opt[i].x,1);
        if (opt[i].kind==2)
          insert(1,1,size,opt[i].x,-1);
        if (opt[i].kind==3) 
          {
            ans=0;
            querynum(1,1,size,opt[i].x);
            printf("%d\n",ans);
          }
        if (opt[i].kind==4)
          {
          	rank=0; 
            queryx(1,1,size,f[opt[i].x]);
            printf("%d\n",f[ans]); 
          }
        if (opt[i].kind==5)
          {
          	ans=0;
          	querynum(1,1,size,opt[i].x);
            rank=0;
            queryx(1,1,size,ans-1);
            printf("%d\n",f[ans]);
          }
        if (opt[i].kind==6)
          {
		    ans=0; 
            querynum(1,1,size,opt[i].x);
            rank=0; 
            queryx(1,1,size,ans+node[ansi]);
            printf("%d\n",f[ans]);
          }
	  }
	fclose(stdin);
	fclose(stdout);
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                                                                                                                                                                                                  Lcomyn
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值