• P5556 【圣剑护符】


    ♗Wendigo♝ 在大约一周前10min用线性基爆切此题。蒟蒻初学线性基,想起此事,便来一试

    如果不会线性基,那么我推荐神♗Wendigo♝的线性基讲解 提交日报了,但是管理员莫名鸽子

    这题可以用线性基推出一个结论:如果一个元素未能成功插入线性基,那么必然存在两个不相等的子集,使得两个子集的属性值相同。

    粗略证明一下:如果未能成功插入元素 (x) ,说明原本那些数中某个集合的异或值等于 (x) ,那么那个集合与 (x) 就是 (2) 个不相等且属性值相同的子集。

    又由于线性基最多 (log S) 个元素,所以如果两点间距离大于 (30) ,必然有元素不能成功插入,直接输出 YES

    修改,可以树剖完弄个树状数组维护区间异或和单点查询,就好了。因为插入线性基只需要单点查询 好像比较显然,没啥别的好说了qwq

    查询,刚刚距离大于 (30) 的情况说过了。如果小于 (30) 暴力跳就好了,毕竟 (30) 比较小。然后应用上面那个结论,看看是否有元素未成功插入。

    所以这题难点就是用线性基发现那个性质,其余都比较显然,而且代码超好写的。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    #define int long long
    #define rint register int
    //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    //char buf[1<<21],*p1=buf,*p2=buf;
    inline int rd() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    	return x*f;
    }
    const int N=100010;
    int n,q,v[N];
    struct edge {
    	int to,nxt;
    }e[N<<1];
    int head[N],num_edge;
    int top[N],fa[N],dfn[N],timer,dep[N],siz[N],son[N];
    bool can;
    int d[40];
    namespace BIT {
    	int tr[N];
    	#define lt(i) (i&(-i))
    	void add(int x,int d) {for(rint i=x;i<=n;i+=lt(i))tr[i]^=d;}
    	void upd(int l,int r,int d){add(l,d),add(r+1,d);}
    	int ask(int x) {
    		int res=0;
    		for(rint i=x;i>0;i-=lt(i))res^=tr[i];
    		return res;
    	}
    }
    void addedge(int from,int to) {
    	++num_edge;
    	e[num_edge].nxt=head[from];
    	e[num_edge].to=to;
    	head[from]=num_edge;
    }
    void dfs1(int u,int ft) {
    	siz[u]=1;
    	for(rint i=head[u];i;i=e[i].nxt) {
    		int v=e[i].to;
    		if(v==ft)continue;
    		dep[v]=dep[u]+1;
    		fa[v]=u;
    		dfs1(v,u);
    		siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]])son[u]=v;
    	}
    }
    void dfs2(int u,int tp)  {
    	top[u]=tp;
    	dfn[u]=++timer;
    	if(son[u])dfs2(son[u],tp);
    	for(rint i=head[u];i;i=e[i].nxt) {
    		int v=e[i].to;
    		if(v==fa[u]||v==son[u])continue;
    		dfs2(v,v);
    	}
    }
    int LCA(int x,int y) {
    	while(top[x]!=top[y]) {
    		if(dep[top[x]]<dep[top[y]])x^=y^=x^=y;
    		x=fa[top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    void update(int x,int y,int z) {
    	while(top[x]!=top[y]) {
    		if(dep[top[x]]<dep[top[y]])x^=y^=x^=y;
    		BIT::upd(dfn[top[x]],dfn[x],z);
    		x=fa[top[x]];
    	}
    	if(dep[x]>dep[y])x^=y^=x^=y;
    	BIT::upd(dfn[x],dfn[y],z);
    }
    void clear() {
    	can=0;
    	for(rint i=0;i<=30;++i)d[i]=0;
    }
    void add(int x) {
    	bool flg=1;
    	for(rint i=30;~i;--i)
    		if(x&(1<<i)) {
    			if(d[i])x^=d[i];
    			else {d[i]=x,flg=0;break;}
    		}
    	can|=flg;
    }
    bool query(int x,int y) {
    	int lca=LCA(x,y);
    	if(dep[x]+dep[y]-(dep[lca]<<1)>30)return 1;
    	clear();
    	while(dep[x]>dep[lca])add(v[x]^BIT::ask(dfn[x])),x=fa[x];
    	while(dep[y]>dep[lca])add(v[y]^BIT::ask(dfn[y])),y=fa[y];
    	add(v[lca]^BIT::ask(dfn[lca]));
    	return can;
    }
    signed main() {
    	n=rd(),q=rd();
    	for(rint i=1;i<=n;++i)v[i]=rd();
    	for(rint i=1,x,y;i<n;++i)
    		x=rd(),y=rd(),addedge(x,y),addedge(y,x);
    	dep[1]=1,dfs1(1,0),dfs2(1,1);
    	char opt[10];
    	while(q--) {
    		scanf("%s",opt);int x=rd(),y=rd();
    		if(opt[0]=='Q')puts(query(x,y)?"YES":"NO");
    		else update(x,y,rd());
    	} 
    	return 0;
    }
    
    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    置换加密算法
    堆和优先队列的应用
    定时发送邮件小程序
    Hibernate的缓存
    Spring中使用JDBC
    Spring AOP(创建切面)
    处理不可中断阻塞
    SQL语句实例说明
    spring_声明式事务
    Flex_includeIn属性的作用
  • 原文地址:https://www.cnblogs.com/zzctommy/p/12918499.html
Copyright © 2020-2023  润新知