• 省选测试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;
    }
    
  • 相关阅读:
    魔法跳舞链 51Nod
    反射
    JDBC---后端服务器与数据库交互的桥梁
    多线程
    IO流
    继承与重写
    java.util包
    多态
    Java.lang包
    异常
  • 原文地址:https://www.cnblogs.com/Lour688/p/14538993.html
Copyright © 2020-2023  润新知