2019ICPC南京网络赛A-The beautiful values of the palace(找规律,离线+线段树)

题目链接:https://nanti.jisuanke.com/t/41298

题目大意:给出一个n*n的螺旋递减的矩阵,其中只有m个元素是有效的。对于q此询问,找出询问矩阵的有效元素数位之和!!

思路:首先要有一个映射函数,将螺旋递减矩阵映射成真实值,很容易,洛谷有一个类似的题,把当时的代码改一改就好了:https://www.luogu.org/problem/P2239。然后就是求询问区间了。当时想的二维前缀和+容斥,但是一直没有思路,存不下!后来才想到,将询问也离线一下就好了。x坐标排序,然后依次放入,就可以直接求前缀和了,由于保证输入的是右下和左上,因此就可以确定容斥的区间了。注意一个坑点!是数位之和,不是元素值之和。。当时一直求的元素和,一直WA,最后都没搞出来。。好惨啊QAQ,自闭中,比较沮丧

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
      
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
 
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
      
const int MAXN=1e5+10;
const int MAXM=1e6+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

struct SegTree{
	struct Node{
		int l,r,len;
		ll Sum;
	};
	Node Tree[MAXM<<2];
	
	void PushUp(int rt){
		Tree[rt].Sum=Tree[rt<<1].Sum+Tree[rt<<1|1].Sum;
	}
	void Build(int l,int r,int rt){
		Tree[rt].l=l;Tree[rt].r=r;
		Tree[rt].Sum=0;
		if(l==r) return ;
		int mid=(l+r)>>1;
		Build(l,mid,rt<<1);Build(mid+1,r,rt<<1|1);
	}
	void Update(int pos,ll val,int rt){
		if(pos==0) return ;
		if(Tree[rt].l==Tree[rt].r){
			Tree[rt].Sum+=val;
			return ;
		}
		if(pos<=Tree[rt<<1].r) Update(pos,val,rt<<1);
		else Update(pos,val,rt<<1|1);
		PushUp(rt);
	}
	ll Query(int ql,int qr,int rt){
		if(ql>qr) return 0;
		if(ql<=Tree[rt].l&&Tree[rt].r<=qr) return Tree[rt].Sum;
		if(qr<=Tree[rt<<1].r) return Query(ql,qr,rt<<1);
		else if(ql>=Tree[rt<<1|1].l) return Query(ql,qr,rt<<1|1);
		else return 1ll*Query(ql,qr,rt<<1)+Query(ql,qr,rt<<1|1);
	}
	void Show(int rt){
		printf("l=%d r=%d Sum=%lld\n",Tree[rt].l,Tree[rt].r,Tree[rt].Sum);
		if(Tree[rt].l==Tree[rt].r) return ;
		Show(rt<<1);Show(rt<<1|1);
	}
};
struct Point{
	int i,j,val,opt;
	friend int operator < (Point a,Point b){
		if(a.i!=b.i) return a.i<b.i; 
		if(a.j!=b.j) return a.j<b.j;
		return a.opt<b.opt;
	}
};
SegTree Seg;
Point Dots[MAXN],Suf[MAXM];
PII Ask[MAXN][2];
ll Ans[MAXM];
map<PII,int> MP;

ll GetVal(int n,int i,int j){
	ll ans;
    ll mi=min(i,min(j,min(n-i+1,n-j+1)));
    if(i<=j) ans=1ll*mi*(1ll*4*(n-1)-4*mi)+1ll*10*mi-4*n-3+i+j;
    else ans=1ll*mi*(4*n-4*mi)+1ll*2*mi+1-i-j;//模拟过程
    return ans;
}
ll Work(int n,int i,int j){
	ll x=GetVal(n,n+1-i,n+1-j),ans=0;
	while(x){
		ans+=x%10;
		x/=10;
	}return ans;
}
int main(){
	int T;scanf("%d",&T);
	while(T--){
		MP.clear();
		int n,m,q;scanf("%d%d%d",&n,&m,&q);
		for(int i=1;i<=m;++i){
			int x,y;scanf("%d%d",&x,&y);
			Dots[i].i=x;Dots[i].j=y;Dots[i].val=Work(n,x,y);
		}
		for(int i=1;i<=q;++i){
			int x1,x2,y1,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			Ask[i][0]=make_pair(x1,y1);Ask[i][1]=make_pair(x2,y2);
		}
		Seg.Build(1,MAXM,1);
		int sum=m+4*q;
		for(int i=1;i<=m;++i){//共m个有效点 
			int x=Dots[i].i,y=Dots[i].j;
			Suf[i].i=x;Suf[i].j=y;Suf[i].val=Dots[i].val;Suf[i].opt=1;
		}
		for(int i=1;i<=q;++i){//共q个询问点 
			Suf[i+m].i=Ask[i][0].first-1;Suf[i+m].j=Ask[i][0].second-1;Suf[i+m].opt=2;
			Suf[i+m+q].i=Ask[i][0].first-1;Suf[i+m+q].j=Ask[i][1].second;Suf[i+m+q].opt=2;
			Suf[i+m+2*q].i=Ask[i][1].first;Suf[i+m+2*q].j=Ask[i][0].second-1;Suf[i+m+2*q].opt=2;
			Suf[i+m+3*q].i=Ask[i][1].first;Suf[i+m+3*q].j=Ask[i][1].second;Suf[i+m+3*q].opt=2;
		}sort(Suf+1,Suf+sum+1);//将询问点排序 
//		for(int i=1;i<=sum;++i) printf("i=%d j=%d val=%lld opt=%d\n",Suf[i].i,Suf[i].j,Suf[i].val,Suf[i].opt);
		for(int i=1;i<=sum;++i){
			int x=Suf[i].i,y=Suf[i].j,val=Suf[i].val,opt=Suf[i].opt;
			if(opt==1){//是一个宫殿 
				Seg.Update(y,val,1);
			}
			else{//是一个起点||其他询问点||终点,刷新答案后不用换 
				Ans[i]=Seg.Query(1,y,1);
				MP[make_pair(x,y)]=i;
//				printf("x=%d y=%d Ans=%lld\n",x,y,Ans[MP[make_pair(x,y)]]);
			}
		}
		//Ans=[x2][y2]-[x2][y1-1]-[x1-1][y2]+[x1-1][y1-1];
		for(int i=1;i<=q;++i){
			PII str=Ask[i][0],ed=Ask[i][1];
			ll ans=Ans[MP[make_pair(ed.first,ed.second)]]-Ans[MP[make_pair(ed.first,str.second-1)]]-Ans[MP[make_pair(str.first-1,ed.second)]]+Ans[MP[make_pair(str.first-1,str.second-1)]];
//			printf("%lld - %lld - %lld + %lld = %lld\n",Ans[MP[make_pair(ed.first,ed.second)]],Ans[MP[make_pair(ed.first,str.second-1)]],Ans[MP[make_pair(str.first-1,ed.second)]],Ans[MP[make_pair(str.first-1,str.second-1)]],ans);
			printf("%lld\n",ans);
		}
	}
}
/*
2
3 4 4
1 1
2 2
3 3
2 3
1 1 1 1
2 2 3 3
1 1 3 3
1 2 2 3

5 5 4
1 1
2 3
3 3
4 4
5 2
1 1 3 3
2 2 3 3
4 2 4 4
3 1 5 5
*/

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值