省选测试37

小A的树

线段树维护点集中最远点对

  • 将点按dfn序排成序列,类似于「超级钢琴」或者「异或粽子」,每个点找到与在他前面的点集中距离最远的点,将这个距离扔进堆里,每从堆中取出一个的时候,区间也被分开成两半,再加进2个值即可,这样最终堆里会有 (n+k) 个点,空间复杂度正确

  • 线段树维护的话,就像维护联通块直径一样就好了,查一次大概是 (log^2) 的(线段树的log套一个树剖的(半个)log),总时间复杂度 (O(nlog+klog^2))

  • 另外有一点,如果序列不按dfn排序,直接按点的编号排成序列,然后一样用线段树维护,那么时间会慢一倍(可能是树剖的时候dfn不连续了,导致常数增大)

Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn=2e5+10;
int n,cnt,K,Time;
int top[maxn],dfn[maxn],Ref[maxn],head[maxn],siz[maxn],son[maxn],fa[maxn],dep[maxn];
ll dis[maxn];
struct Edge{ int to,nxt,val; }e[maxn*2];
struct Seg{ int x,y; }t[maxn*4];
struct Node{
	int x,l,r,mid;
	ll val;
	bool operator < (const Node &B) const {
		return val<B.val;
	}
};
priority_queue <Node> q;

int read(int x=0,bool f=0,char ch=getchar()){
	for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
	return f?-x:x;
}

void add(int x,int y,int z){
	e[++cnt]=(Edge){y,head[x],z};
	head[x]=cnt;
}

void dfs1(int x,int prt){
	siz[x]=1,fa[x]=prt,dep[x]=dep[prt]+1;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==prt) continue;
		dis[y]=dis[x]+e[i].val;
		dfs1(y,x);
		siz[x]+=siz[y];
		if(!son[x] || siz[y]>siz[son[x]]) son[x]=y;
	}
}

void dfs2(int x,int tp){
	top[x]=tp,dfn[x]=++Time,Ref[Time]=x;
	if(son[x]) dfs2(son[x],tp);
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y!=fa[x]&&y!=son[x]) dfs2(y,y);
	}
}

int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

ll Dis(int x,int y){
	return dis[x]+dis[y]-dis[lca(x,y)]*2;
}

Seg up(const Seg &A,const Seg &B){
	if(!A.x) return B;
	if(!B.x) return A;
	int tmp[4]={A.x,A.y,B.x,B.y};
	int xx,yy;
	ll mx=-1e18;
	for(int i=0;i<=3;++i){
		for(int j=i+1;j<=3;++j){
			ll nd=Dis(tmp[i],tmp[j]);
			if(nd>mx) mx=nd,xx=tmp[i],yy=tmp[j];
		}
	}
	return (Seg){xx,yy};
}

void build(int rt,int l,int r){
	if(l==r) return t[rt]=(Seg){Ref[l],Ref[l]},void();
	int mid=(l+r)/2;
	build(rt*2,l,mid),build(rt*2+1,mid+1,r);
	t[rt]=up(t[rt*2],t[rt*2+1]);
}

Seg ask(int rt,int l,int r,int x,int y){
	if(x<=l&&r<=y) return t[rt];
	int mid=(l+r)/2; Seg ret=(Seg){0,0};
	if(x<=mid) ret=up(ret,ask(rt*2,l,mid,x,y));
	if(y>mid)  ret=up(ret,ask(rt*2+1,mid+1,r,x,y));
	return ret;
}

void Push(int x,int l,int r){
	Seg now=ask(1,1,n,l,r);
	ll d1=Dis(x,now.x),d2=Dis(x,now.y);
	if(d1>d2) q.push((Node){x,l,r,dfn[now.x],d1});
	else q.push((Node){x,l,r,dfn[now.y],d2});
}

int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read(),K=read();
	for(int i=1;i<n;++i){
		int x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);
	}
	dfs1(1,0),dfs2(1,1);
	build(1,1,n);
	for(int i=2;i<=n;++i) Push(Ref[i],1,i-1);
	for(int i=1;i<=K;++i){
		int x=q.top().x;
		int l=q.top().l;
		int r=q.top().r;
		int mid=q.top().mid;
		printf("%lld
",q.top().val);
		q.pop();
		if(l<mid) Push(x,l,mid-1);
		if(mid<r) Push(x,mid+1,r);
	}
	return 0;
}

小B的序列

势能分析线段树

当进行修改操作的时候,只有当前的操作是对于区间所有数起相同效应的时候,才能统一修改,否则递归到子区间处理

具体的,‘&’的数的2进制位上的0会影响序列中数的数值,‘|’的数的二进制位上的1会造成影响,那么我们当前区间的所有数每个这样的二进制位上的数都是0或都是1的话,我们就可以直接改这一整个区间,打上标记,这个的判断以及答案的维护我们需要维护5个东西,区间max,区间and和,区间or和,and标记,or标记。

至于打标记,强制先and后or,假设我们一个区间,既有and标记,又有or标记,那么这样处理:

(A and B) or C) and D = (A and (B and D)) or (C and D)

(A and B) or C) or D = (A and B) or (C or D)

通俗来说:

  • 如果要加and标记x,那么当前的and标记和or标记都要and上x

  • 如果要加or标记x,那么只有当前or标记需要or上x

Code
#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5+10;
const int maxs=(1<<20)-1;
int n,q,a[maxn];
struct Node{
	int sor,sand,mx,lzor,lzand;
}t[maxn*4];

int read(int x=0,bool f=0,char ch=getchar()){
	for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
	return f?-x:x;
}

void up(int x){
	t[x].mx=max(t[x*2].mx,t[x*2+1].mx);
	t[x].sor=t[x*2].sor|t[x*2+1].sor;
	t[x].sand=t[x*2].sand&t[x*2+1].sand;
}

void upd(int x,int va,int vo){
	t[x].mx&=va,t[x].sor&=va,t[x].sand&=va;
	t[x].mx|=vo,t[x].sor|=vo,t[x].sand|=vo;
	t[x].lzand&=va,t[x].lzor&=va;
	t[x].lzor|=vo;
}

void down(int x){
	upd(x*2,t[x].lzand,t[x].lzor);
	upd(x*2+1,t[x].lzand,t[x].lzor);
	t[x].lzand=maxs,t[x].lzor=0;
}

void build(int rt,int l,int r){
	t[rt].lzand=maxs;
	if(l==r) return t[rt]=(Node){a[l],a[l],a[l],0,maxs},void();
	int mid=(l+r)/2;
	build(rt*2,l,mid),build(rt*2+1,mid+1,r);
	up(rt);
}

void And(int rt,int l,int r,int x,int y,int v){
	if(x>y) return;
	if(l==r || (x<=l&&r<=y&&(v|(t[rt].sor^t[rt].sand))==v)){
		t[rt].mx&=v;
		t[rt].sor&=v;
		t[rt].sand&=v;
		t[rt].lzand&=v;
		t[rt].lzor&=v;
		return;
	}
	int mid=(l+r)/2;
	if(t[rt].lzor || t[rt].lzand!=maxs) down(rt);
	if(x<=mid) And(rt*2,l,mid,x,y,v);
	if(y>mid)  And(rt*2+1,mid+1,r,x,y,v);
	up(rt);
}

void Or(int rt,int l,int r,int x,int y,int v){
	if(x>y) return;
	if(l==r || (x<=l&&r<=y&&((v&(t[rt].sor^t[rt].sand))==0))){
		t[rt].mx|=v;
		t[rt].sor|=v;
		t[rt].sand|=v;
		t[rt].lzor|=v;
		return;
	}
	int mid=(l+r)/2;
	if(t[rt].lzor || t[rt].lzand!=maxs) down(rt);
	if(x<=mid) Or(rt*2,l,mid,x,y,v);
	if(y>mid)  Or(rt*2+1,mid+1,r,x,y,v);
	up(rt);
}

int Ask(int rt,int l,int r,int x,int y){
	if(x<=l&&r<=y) return t[rt].mx;
	int mid=(l+r)/2,ret=0;
	if(t[rt].lzor || t[rt].lzand!=maxs) down(rt);
	if(x<=mid) ret=max(ret,Ask(rt*2,l,mid,x,y));
	if(y>mid)  ret=max(ret,Ask(rt*2+1,mid+1,r,x,y));
	return ret;
}

int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n=read(),q=read();
	for(int i=1;i<=n;++i) a[i]=read();
	build(1,1,n);
	for(int i=1;i<=q;++i){
		int opt=read();
		if(opt==1){
			int l=read(),r=read(),x=read();
			And(1,1,n,l,r,x);
		}
		else if(opt==2){
			int l=read(),r=read(),x=read();
			Or(1,1,n,l,r,x);
		}
		else{
			int l=read(),r=read();
			printf("%d
",Ask(1,1,n,l,r));
		}
	}
	return 0;
}

小C的利是

行列式,构建k个矩阵,对于每个矩阵,使其值的指数为当前矩阵的值,从而实现将 (sum) 变为 (prod)

Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;

int n,K;
int a[105][105],s[105][105],xy[105][105];

bool isp(int x){
	for(int i=2;i*i<=x;++i) if(!(x%i)) return 0;
	return 1;
}

int fp(int a,int x,int mod,int ans=1){
	for(;x;x>>=1,a=(ll)a*a%mod)
		if(x&1) ans=(ll)ans*a%mod;
	return ans;
}

int det(int n,int mod){
	int ans=1;
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			if(xy[j][i]){
				for(int k=i;k<=n;++k) swap(xy[i][k],xy[j][k]);
				ans=-ans;
				break;
			}
		}
		ans=(ll)ans*xy[i][i]%mod;
		if(!ans) break;
		int inv=fp((xy[i][i]%mod+mod)%mod,mod-2,mod);
		for(int j=i+1;j<=n;++j){
			int tmp=(ll)xy[j][i]*inv%mod;
			for(int k=i;k<=n;++k) xy[j][k]=(xy[j][k]-(ll)tmp*xy[i][k]%mod)%mod;
		}
	}
	return (ans%mod+mod)%mod;
}

int main(){
	freopen("luckymoney.in","r",stdin);
	freopen("luckymoney.out","w",stdout);
	scanf("%d%d",&n,&K);
	for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%d",&a[i][j]);
	int p=K*317913+1,g,sum=0;
	while(!isp(p)) p+=K;
	for(g=2;g<p;++g) if(fp(g,K,p)==1) break;
	for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) s[i][j]=fp(i+j,j,998244353)%p;
	for(int now=0,x=1;now<K;++now,x=(ll)x*g%p){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
			        if(a[i][j]!=-1) xy[i][j]=(ll)fp(x,a[i][j],p)*s[i][j]%p;
				else xy[i][j]=0;
			}
		}
		(sum+=det(n,p))%=p;
	}
	puts(sum?"Yes":"No");
	return 0;
}
原文地址:https://www.cnblogs.com/Lour688/p/14538993.html