JZOJ6825. 【2020.10.17提高组模拟】prime

20 篇文章 0 订阅
14 篇文章 0 订阅

Description

在这里插入图片描述

  • n < = 2 e 5 , ∣ v ∣ ≤ 5000 n<=2e5,|v|\le5000 n<=2e5,v5000

Solution

  • 对于虚数的相乘,是模长相乘、极角相加的,最后是质数模长 ∣ p ∣ 2 |p|^2 p2,所以最多有两个数相乘形成这个质数。
  • 不妨考虑对于还未到两个因子的复数暴力乘,由于势能一定,所以时间是可以满足的。
  • 又因为有区间赋值,所以我们可以用一个平衡树来进行块的合并(set也可以)。
  • 再考虑不贡献模长的四种数, 1 , − 1 , i , − i 1,-1,i,-i 1,1,i,i,多维护 4 4 4个东西表示四个方向上的质数有哪些,以及tag还要有当前转了多少度。
  • 以上可以用线段树做到两个log,看起来线段树要好打一点(
  • 注意一系列tag下放对于答案的影响(至于其他若干弱智错误每一个就要花我十几分钟)。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<ctime>
#define maxn 200005
#define maxm 50000005
using namespace std;

int n,q,i,j,k;
struct Z{int x,y,c;Z(int _x=0,int _y=0,int _c=0){x=_x,y=_y,c=_c;}} fx[4];
Z operator*(Z a,Z b){
	if (a.c+b.c>2) return Z(0,0,3);
	return Z(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x,a.c+b.c);
}
Z a[maxn],t[maxn*4]; int c[maxn*4],bz[maxn*4],s[maxn*4][4],tag[maxn*4];

void read(int &x){
	x=0; char ch=getchar(); int t=1;
	for(;(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
	if (ch=='-') t=-1,ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	x=x*t;
}

int tot,pri[maxm],isp[maxm];
void getpri(){
	isp[0]=isp[1]=1;
	for(i=2;i<maxm;i++){
		if (!isp[i]) pri[++tot]=i;
		for(j=1;j<=tot&&i*pri[j]<maxm;j++){
			isp[i*pri[j]]=1;
			if (i%pri[j]==0) break;
		}
	}
}

void check(int x,int l,int r){
	Z a=t[x]*fx[tag[x]];
	s[x][0]=s[x][1]=s[x][2]=s[x][3]=0;
	if (a.x==0&&!isp[abs(a.y)]) s[x][(a.y>0)?1:3]+=(r-l+1);
	if (a.y==0&&!isp[abs(a.x)]) s[x][(a.x>0)?0:2]+=(r-l+1);
}

void upd(int x){
	int l=x<<1,r=x<<1^1;
	c[x]=c[l]+c[r];
	s[x][0]=s[l][0]+s[r][0],s[x][1]=s[l][1]+s[r][1];
	s[x][2]=s[l][2]+s[r][2],s[x][3]=s[l][3]+s[r][3];
}

int tmp[4];
void downtag(int x,int l,int r){
	if (bz[x]){
		int mid=(l+r)>>1;
		t[x<<1]=t[x],bz[x<<1]=1,tag[x<<1]=tag[x],c[x<<1]=(c[x]>0)*(mid-l+1);
		t[x<<1^1]=t[x],bz[x<<1^1]=1,tag[x<<1^1]=tag[x],c[x<<1^1]=(c[x]>0)*(r-mid);
		for(int k=0;k<4;k++) {
			s[x<<1][k]=(s[x][k]>0)*(mid-l+1);
			s[x<<1^1][k]=(s[x][k]>0)*(r-mid);
		}
		bz[x]=0,t[x]=Z(0,0,0),tag[x]=0;
	} else 
	if (tag[x]) {
		for(int k=0;k<4;k++) tmp[(k+tag[x])%4]=s[x<<1][k];
		memcpy(s[x<<1],tmp,sizeof(tmp));
		for(int k=0;k<4;k++) tmp[(k+tag[x])%4]=s[x<<1^1][k];
		memcpy(s[x<<1^1],tmp,sizeof(tmp));
		(tag[x<<1]+=tag[x])%=4,(tag[x<<1^1]+=tag[x])%=4,tag[x]=0;
	}
}

void maketree(int x,int l,int r){
	if (l==r) {
		bz[x]=1;
		if (!a[l].x&&!a[l].y) c[x]=0; else {
			c[x]=1;
			if (!a[l].x&&abs(a[l].y)==1) t[x]=Z(1,0,0),tag[x]=(a[l].y<0)?3:1; else 
			if (!a[l].y&&abs(a[l].x)==1) t[x]=Z(1,0,0),tag[x]=(a[l].x>0)?0:2; else 
				t[x]=a[l],t[x].c=1,tag[x]=0;
		}
		check(x,l,r);
		return;
	}
	int mid=(l+r)>>1;
	maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
	upd(x);
}

void add(int x,int l,int r,int L,int R,int d){
	if (l>R||r<L) return;
	if (L<=l&&r<=R) {
		for(int k=0;k<4;k++) tmp[(k+d)%4]=s[x][k];
		memcpy(s[x],tmp,sizeof(tmp)),(tag[x]+=d)%=4;
		return;
	}
	downtag(x,l,r);
	int mid=(l+r)>>1;
	add(x<<1,l,mid,L,R,d),add(x<<1^1,mid+1,r,L,R,d);
	upd(x);
}

void mul(int x,int l,int r,int L,int R,Z v){
	if (l>R||r<L) return;
	if (!c[x]) return;
	if (L<=l&&r<=R&&bz[x]) {
		t[x]=t[x]*v,c[x]=(t[x].c<=2)*(r-l+1),check(x,l,r);
		return;
	}
	downtag(x,l,r);
	int mid=(l+r)>>1;
	mul(x<<1,l,mid,L,R,v),mul(x<<1^1,mid+1,r,L,R,v);
	upd(x);
}

void cover(int x,int l,int r,int L,int R,Z v){
	if (l>R||r<L) return;
	if (L<=l&&r<=R) {
		bz[x]=1,t[x]=v,tag[x]=0,c[x]=(t[x].c<=2)*(r-l+1);
		check(x,l,r);
		return;
	}
	downtag(x,l,r);
	int mid=(l+r)>>1;
	cover(x<<1,l,mid,L,R,v);
	cover(x<<1^1,mid+1,r,L,R,v);
	upd(x);
}

int find(int x,int l,int r,int L,int R){
	if (l>R||r<L) return 0;
	if (L<=l&&r<=R) return s[x][0];
	downtag(x,l,r);
	int mid=(l+r)>>1;
	return find(x<<1,l,mid,L,R)+find(x<<1^1,mid+1,r,L,R);
}

int main(){
	freopen("ceshi.in","r",stdin);
	freopen("ceshi.out","w",stdout);
//	freopen("prime.in","r",stdin);
//	freopen("prime.out","w",stdout);
	read(n),read(q),getpri();
	fx[0]=Z(1,0,0),fx[1]=Z(0,1,0),fx[2]=Z(-1,0,0),fx[3]=Z(0,-1,0);
	for(i=1;i<=n;i++) read(a[i].x),read(a[i].y);
	maketree(1,1,n);
	while (q--){
		int opt,l,r; Z v; read(opt),read(l),read(r);
		if (opt==1) {
			read(v.x),read(v.y);
			if (!v.x&&!v.y) v.c=3; else 
			if (!v.x&&abs(v.y)==1||!v.y&&abs(v.x)==1) v.c=0; else 
				v.c=1;
			cover(1,1,n,l,r,v);
		} else 
		if (opt==2){
			read(v.x),read(v.y);
			if (!v.x&&!v.y) v.c=3,mul(1,1,n,l,r,v); else
			if (!v.x&&abs(v.y)==1) add(1,1,n,l,r,(v.y>0)?1:3); else
			if (!v.y&&abs(v.x)==1) add(1,1,n,l,r,(v.x>0)?0:2); else 
				v.c=1,mul(1,1,n,l,r,v);
		}
		else printf("%d\n",find(1,1,n,l,r));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值