炸弹

题意:在一些坐标上有一些炸弹。。有两个操作

          1、移动炸弹,,把一个炸弹从一个坐标移到另一个坐标。

          2、询问一段坐标中所有炸弹坐标中大坐标减去小坐标。


思路:离散化,把所有可能出现的坐标点找出来排好序后,去重。

           把所有坐标都映射到他所对应的在数组的索引号,用map实现。

           用res记录段的坐标差和,sum记录所有坐标之和,cnt记录有多少个地雷。

          更新res=res1+res2+cnt1*sum2-cnt2*sum1;

          sum=sum1+sum2;

          cnt=cnt1+cnt2;

          查找时的结果res要依靠sum和cnt所以返回类型为tree,并且更新只在返回的结构体中更新。



#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>

using namespace std;


#define N 100010
int n,q;
struct tree
{
	int left,right;
	int mid;
	long long res,sum,cnt;
}t[N*12];
map<long long,int> m;

struct car 
{
	int flag;
	long long  from,to;
}ask[N];
long long hi[2*N];
int nhi;
long long go[N];

void build(int cur,int l,int r)
{
	t[cur].mid=(l+r)>>1;
	t[cur].left=l;
	t[cur].right=r;
	t[cur].res=t[cur].sum=t[cur].cnt=0;
	if (l!=r)
	{
		build(cur<<1,l,t[cur].mid);
		build(cur<<1|1,t[cur].mid+1,r);
	}
}

void add(int cur,int l)
{
	if (t[cur].left==t[cur].right)
	{
		t[cur].sum=hi[l];
		t[cur].cnt=1;
		t[cur].res=0;
		return;
	}
	
	int L=cur<<1,R=cur<<1|1;
	int m=t[cur].mid;
	if (l<=m)
		add(L,l);
	else 
		add(R,l);
	
	t[cur].sum=t[L].sum+t[R].sum;
	t[cur].cnt=t[L].cnt+t[R].cnt;
	t[cur].res=t[L].res+t[R].res+t[L].cnt*t[R].sum-t[R].cnt*t[L].sum;
}

void remove(int cur,int l)
{
	if (t[cur].left==t[cur].right)
	{
		t[cur].sum=0;
		t[cur].cnt=0;
		t[cur].res=0;
		return;
	}
	
	int L=cur<<1,R=cur<<1|1;
	int m=t[cur].mid;
	if (l<=m)
		remove(L,l);
	else 
		remove(R,l);
	
	t[cur].sum=t[L].sum+t[R].sum;
	t[cur].cnt=t[L].cnt+t[R].cnt;
	t[cur].res=t[L].res+t[R].res+t[L].cnt*t[R].sum-t[R].cnt*t[L].sum;
}

tree query(int cur,int l,int r)
{
	if (l<=t[cur].left && r>=t[cur].right)
	{
		return t[cur];
	}
	
	int m=t[cur].mid;
	int L=cur<<1,R=cur<<1|1;
	if (r<=m)
		return query(L,l,r);
	else if (l>m)
		return query(R,l,r);
	
	tree LL=query(L,l,r);
	tree RR=query(R,l,r);
	tree ans;
	ans.res=LL.res+RR.res+RR.sum*LL.cnt-LL.sum*RR.cnt;
	ans.sum=LL.sum+RR.sum;
	ans.cnt=LL.cnt+RR.cnt;
	return ans;
}


int main()
{
	freopen("in.txt","r",stdin);
	int i,j,k;
	int nc;
	cin>>nc;
	while (nc--)
	{
		scanf("%d",&n);
		for (i=0;i<n;i++)
		{
			scanf("%d",&k);
			go[i]=k;
			hi[i]=k;
		}
		nhi=n;
		scanf("%d",&q);
		for (int ii=0;ii<q;ii++)
		{
			scanf("%d%d%d",&k,&i,&j);
			if (k==1)
			{
				go[i-1]+=j;
				hi[nhi++]=go[i-1];
			}
			else
			{
				hi[nhi++]=i;
				hi[nhi++]=j;
			}
			ask[ii].flag=k;
			ask[ii].from=i;
			ask[ii].to=j;
		}
		for (i=0;i<n;i++)
			go[i]=hi[i];
		sort(hi,hi+nhi);
		
		j=1;
		for (i=1;i<nhi;i++)
			if (hi[i]!=hi[i-1])
				hi[j++]=hi[i];
		nhi=j;
		for (i=0;i<nhi;i++)
			m[hi[i]]=i;
		build(1,0,nhi-1);
		
		for (i=0;i<n;i++)
			add(1,m[go[i]]);
		
		
		for (i=0;i<q;i++)
		{
			if (ask[i].flag==1)
			{
				remove(1,m[go[ask[i].from-1]]);
			//	cout<<go[ask[i].from]<<' ';
				go[ask[i].from-1]+=ask[i].to;
			//	cout<<go[ask[i].from]<<endl;
				add(1,m[go[ask[i].from-1]]);
			}
			else
			{
			//	cout<<ask[i].from<<' '<<ask[i].to<<' '<<m[ask[i].from]<<' '<<m[ask[i].to]<<endl;
				printf("%lld\n",query(1,m[ask[i].from],m[ask[i].to]).res);
			}
		}
		
	}
	
	
	return 0;
}



贪玩的xie

Time Limit: 5000 ms Memory Limit: 65535 kB Solved: 25 Tried: 222

Submit

Status

Best Solution

Back

Description

Xie是一个很无聊的人,他最喜欢的事就是随手拿一些可怕的东西来玩。
现在,xie在x轴上放了n个地雷。第一个地雷的坐标是x1,第二个的坐标是x2,..,第n个的坐标是xn。
现在xie有m个关于这些地雷的询问,每个询问分为以下两类:
1.把第pj个地雷从所在的位置xpj移动到xpj+dj。数据保证,每次移动之后,每个地雷的坐标都不同。
2.为了评估地雷爆炸的威力,计算线段[lj,rj]上的每对地雷的距离之和。也就是说,要计算

Input

第一行一个整数T,表示数据组数。
每组数据第一行一个整数n(1<=n<=10^5),地雷的数量。第二行包含n个不同的整数x1,x2,…,xn,表示地雷的坐标。

第三行有一个整数m,代表询问的数量(1<=m<=10^5).接下来m行描述这些询问。第j行的第一个整数tj(1<=tj&lt;=2),表示询问的类型。如果tj=1,那么接下来会有两个整数pj和 dj(1<=pj<=n,|dj|<=1000)。如果tj=2,那么会有两个整数lj,rj(-10^9 < lj <= rj <= 10^9)

保证任意时刻,所有的地雷都在不同的坐标上。

Output

对于每个第2类型的询问,输出一行答案。输出答案请按询问出现的顺序来输出。

Sample Input

1
8
36 50 28 -75 40 -60 -95 -48
20
2 -61 29
1 5 -53
1 1 429
1 5 130
2 -101 -71
2 -69 53
1 1 404
1 5 518
2 -101 53
2 50 872
1 1 -207
2 -99 -40
1 7 -389
1 6 -171
1 2 464
1 7 -707
1 1 -730
1 1 560
2 635 644
1 7 -677

Sample Output

176
20
406
1046
1638
156
0


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值