【2022省选模拟】选拔——点分治,哈希,bitset

这又是个假链接

题目描述

在这里插入图片描述
在这里插入图片描述

题解

考虑用点分治解决这个问题。

对于一条路径,它会被分治中心切成左半部分和右半部分。我们称两个字符串可以拼接,当且仅当两个字符串都在树上出现至少一次,并且存在某两个出现位置与同一个点分治中心相连且处在其不同子树中。

我们如果能够快速判断两个字符串是否可以拼接,那么就能枚举询问串从哪个位置断开,然后判断是否出现。枚举过程可以用哈希做到 O ( s ) O(s) O(s)

我们可以想到对每个字符串记录它与哪些分治中心相连,然后判断集合是否有交,但是这样显然不能处理“处在不同子树”的限制。
稍微改进一下,我们对分治中心的每个子树进行编号,然后把字符串“左半部分”和“右半部分”用不同的方式处理。左半部分,对每个字符串记录出现的子树编号;右半部分,记录能与它拼接的子树编号。

举个例子:
在这里插入图片描述
我们规定a串为左边,b串为右边,那么a串记录的编号为 { 2 } \{2\} {2},b串记录的编号为 { 1 , 2 , 4 } \{1,2,4\} {1,2,4}。实际上由于左右交换后等价,所以右半部分只记录子树编号小于它的也不会算漏,比如b串可以只记录 { 1 , 2 } \{1,2\} {1,2}

然后判断左边和右边的集合是否有交,可以用 bitset,这么做下来复杂度是 O ( n 2 log ⁡ n w ) O(\frac{n^2\log n}{w}) O(wn2logn)

考虑优化这个过程,我们发现每个哈希值存的bitset里的元素其实很稀疏,并且是成段的,所以考虑分块,直接每 w = 64 w=64 w=64 个点分一块做状压,预先建好点分树,然后对每一块单独求解。对于记录编号不在块内的,我们不去遍历。

这样做下来的效果很神奇,复杂度可以直接降为 O ( n 2 w ) O(\frac{n^2}{w}) O(wn2)
简单证明一下,我们先考虑左半部分,显然每棵子树编号只会出现在一个块内,所以只会遍历一次,总遍历次数是 O ( n log ⁡ n ) O(n\log n) O(nlogn)
对于右半部分,我们考虑每个节点的遍历次数:若一个节点处在某个分治中心的子树下,那么记录的编号数量最多为这个分治中心的度数。由于每个分治中心的子树编号连续,所以遍历次数为 度 数 w \frac{度数}{w} w;一个节点上头最多有 log ⁡ n \log n logn 个分治中心,它们的度数和最多为 n n n,所以一个节点的遍历次数最多为 max ⁡ ( n w , log ⁡ n ) \max(\frac{n}{w},\log n) max(wn,logn),总遍历次数最多 n 2 w \frac{n^2}{w} wn2

代码

#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=30004;
const ll INF=1e18;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	ptf[lpt=1]=x%10;
	while(x>9)x/=10,ptf[++lpt]=x%10;
	while(lpt)putchar(ptf[lpt--]^48);
	if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}

const uns ll cg=31;
struct edge{
	int v,to,c;edge(){}
	edge(int V,int T,int C){v=V,to=T,c=C;}
}e[MAXN<<1];
int EN,G[MAXN];
inline void addedge(int u,int v,int c){
	e[++EN]=edge(v,G[u],c),G[u]=EN;
	e[++EN]=edge(u,G[v],c),G[v]=EN;
}
vector<int>D[MAXN];
int n,m,k;
string s[MAXN];
int ct,mn,root,can[60][MAXN];
bool dl[MAXN],as[MAXN];
uns ll pr[MAXN],sf[MAXN],mi[MAXN];
int IN,L,R;
uns ll mp[MAXN<<2],pm[MAXN<<2];
map<uns ll,int>st;
vector<int>hd[MAXN],tl[MAXN];
inline int pdfs(int x,int fa,int S){
	int siz=1,cp=0;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v,sv=233;
		if(v==fa||dl[v])continue;
		sv=pdfs(v,x,S),siz+=sv,cp=max(cp,sv);
	}cp=max(cp,S-siz);
	if(cp<mn)mn=cp,ct=x;
	return siz;
}
inline void pdfs2(int x,int fa,uns ll hs,int dep){
	if(st.find(hs)!=st.end())can[dep][x]=st[hs];
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(v==fa||dl[v])continue;
		pdfs2(v,x,hs*cg+e[i].c,dep);
	}
}
inline int build(int x,int S,int dep){
	if(S<0)S=pdfs(x,0,S);
	ct=0,mn=1e9,pdfs(x,0,S),x=ct,dl[x]=1;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(!dl[v])IN++,pdfs2(v,x,e[i].c,dep);
	}
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(!dl[v])D[x].push_back(build(v,-1,dep+1));
	}return x;
}
int DEP;
inline void dfs1(int x,int fa,uns ll d){
	if(can[DEP][x]>0)mp[can[DEP][x]]|=d;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(v==fa||dl[v])continue;
		dfs1(v,x,d);
	}
}
inline void dfs2(int x,int fa,uns ll d){
	if(can[DEP][x])pm[can[DEP][x]]|=d;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(v==fa||dl[v])continue;
		dfs2(v,x,d);
	}
}
inline void solve(int x,int dep){
	if(IN>=R)return;
	uns ll g=0;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(dl[v])continue;
		IN++,DEP=dep;
		if(g)dfs2(v,x,g);
		if(IN>=L&&IN<=R)dfs1(v,x,1ull<<(IN-L)),g|=(1ull<<(IN-L));
	}dl[x]=1;
	for(int v:D[x])solve(v,dep+1);
}
signed main()
{
	freopen("selection.in","r",stdin);
	freopen("selection.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read(),c=getchar()-'a'+1;
		addedge(u,v,c);
	}
	m=read(),mi[0]=1;
	for(int i=1;i<=30002;i++)mi[i]=mi[i-1]*cg;
	for(int i=1;i<=m;i++){
		cin>>s[i];
		int len=s[i].length();
		pr[0]=sf[len+1]=0;
		for(int j=0;j<len;j++)
			pr[j+1]=pr[j]+(s[i][j]-'a'+1)*mi[j];
		for(int j=len-1;j>=0;j--)
			sf[j+1]=sf[j+2]+(s[i][j]-'a'+1)*mi[len-1-j];
		for(int j=0;j<=len;j++)st[pr[j]]=st[sf[j+1]]=0;
	}
	for(auto it=st.begin();it!=st.end();it++)IS=++k;
	for(int i=1;i<=m;i++){
		int len=s[i].length();
		pr[0]=sf[len+1]=0;
		for(int j=0;j<len;j++)
			pr[j+1]=pr[j]+(s[i][j]-'a'+1)*mi[j];
		for(int j=len-1;j>=0;j--)
			sf[j+1]=sf[j+2]+(s[i][j]-'a'+1)*mi[len-1-j];
		hd[i].resize(len+2),tl[i].resize(len+2);
		for(int j=0;j<=len;j++)hd[i][j]=st[pr[j]],tl[i][j+1]=st[sf[j+1]];
	}
	root=build(1,n,0);
	for(R=IN,L=IN-64+1;R>0;R-=64,L-=64){
		IN=0;
		for(int i=1;i<=k;i++)mp[i]=pm[i]=0;
		for(int i=1;i<=n;i++)dl[i]=0;
		mp[st[0]]=pm[st[0]]=(~0ull),solve(root,0);
		for(int i=1;i<=m;i++)if(!as[i]){
			int len=s[i].length();
			for(int j=0;j<=len&&!as[i];j++)
				as[i]|=(mp[hd[i][j]]&pm[tl[i][j+1]])||(pm[hd[i][j]]&mp[tl[i][j+1]]);
		}
	}
	for(int i=1;i<=m;i++)printf(as[i]?"YES\n":"NO\n");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值