• [冲刺国赛2022] 模拟赛12


    牌佬

    题目描述

    给定一棵大小为 \(n\) 的树,判断是否存在三个点 \((x,y,z)\) 满足:

    • \(y\)\((x,z)\) 的简单路径上。
    • \(x+z=2y\)

    \(n\leq 2^{20}\)

    解法

    首先考虑序列上怎么做,我们枚举点 \(y\),然后考察有解的条件。我们把序列上在 \(y\) 左侧的点设置为 \(1\),在 \(y\) 右侧的点设置为 \(0\),那么在值域序列以 \(y\) 中心构成回文串(抵着值域边界)就说明无解。这是因为如果不构成回文串,那么势必有一对对称的 \((0,1)\),满足 \(x+z=2y\),并且在原序列上是经过 \(y\) 的。

    把上面的做法搬到序列上,现在我们要判断对于点 \(y\) 和在子树 \(S\) 处是否有解,把子树 \(S\) 设置为 \(1\) 即可。

    但是把子树设置为 \(1\) 不太好维护,考虑把 \(y\) 在值域序列上的回文串设置为 \(1\),然后判断在子树 \(S\) 中是否出现了正反的回文序列。那么扫描值域,预处理出树上的 \(\tt dfn\) 序,然后用树状数组维护哈希值即可,时间复杂度 \(O(n\log n)\)

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 1100005;
    const int MOD = 998244353;
    #define ll long long
    #define pb push_back
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,cnt,dfn[M],siz[M],pw[M],ip[M],fa[M];
    vector<int> g[M];int k,s[M],b[M];
    struct fenwick
    {
    	int b[M];
    	fenwick() {memset(b,0,sizeof b);}
    	void add(int x,int c)
    	{
    		for(int i=x;i<=n;i+=i&(-i))
    			b[i]=(b[i]+c)%MOD;
    	}
    	int ask(int x)
    	{
    		int r=0;
    		for(int i=x;i>0;i-=i&(-i))
    			r=(r+b[i])%MOD;
    		return r;
    	}
    	int ask(int l,int r)
    	{
    		if(l>r) return 0;
    		return (ask(r)-ask(l-1)+MOD)%MOD;
    	}
    }A,B;
    void dfs(int u,int p)
    {
    	dfn[u]=++cnt;siz[u]=1;fa[u]=p;
    	for(int v:g[u]) if(v^p)
    		dfs(v,u),siz[u]+=siz[v];
    }
    void get(int u,int p)
    {
    	s[++k]=u;
    	for(int v:g[u]) if(v^p)
    		get(v,u);
    }
    void print(int u)
    {
    	for(int v:g[u])
    	{
    		k=0;get(v,u);
    		for(int i=1;i<=k;i++)
    		{
    			int x=2*u-s[i];
    			if(x>=1 && x<=n && b[x])
    			{
    				printf("YES %d %d %d\n",x,u,s[i]);
    				return ;
    			}
    		}
    		for(int i=1;i<=k;i++)
    			b[s[i]]=1;
    	}
    }
    signed main()
    {
    	freopen("gangster.in","r",stdin);
    	freopen("gangster.out","w",stdout);
    	n=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		g[u].pb(v);g[v].pb(u);
    	}
    	dfs(1,0);pw[0]=ip[0]=1;
    	for(int i=1;i<=n;i++)
    	{
    		pw[i]=pw[i-1]*3ll%MOD;
    		ip[i]=ip[i-1]*332748118ll%MOD;
    	}
    	int p=1,q=1,s=2,t=2;
    	for(int i=1;i<=n;i++)
    	{
    		while(p<i) A.add(dfn[p],ip[p]),p++;
    		while(2*i-q>n && q<i) A.add(dfn[q],MOD-ip[q]),q++;
    		while(s<=i) B.add(dfn[s],MOD-pw[s]),s++;
    		while(2*i-t>=1 && t<=n) B.add(dfn[t],pw[t]),t++;
    		for(int v:g[i]) if(v^fa[i])
    		{
    			int l=A.ask(dfn[v],dfn[v]+siz[v]-1);
    			int r=B.ask(dfn[v],dfn[v]+siz[v]-1);
    			l=(ll)l*pw[i]%MOD*pw[i]%MOD;
    			if(l!=r) {print(i);return 0;}
    		}
    	}
    	puts("NO");
    }
    

    唱诗

    题目描述

    给定一棵 \(n\) 个点的 \(\tt trie\) 树,定义子串为 \(\tt trie\) 树上一条从上到下的路径所代表的字符串。

    \(q\) 次询问,每次查询字典序第 \(k\) 小的非空子串,只需要输出子串的 \(\tt ASCII\) 码之和即可。

    \(n\leq 2\cdot 10^5,q\leq 5\cdot 10^5\),保证只会出现 \(26\) 个小写字母。

    解法

    根据 \(\tt trie\) 树可以直接建出广义后缀自动机(还省去了你多模板串建立 \(\tt trie\) 树的过程),然后在后缀自动机上搜就可以找到第 \(k\) 小的串(“搜”类似于线段树二分),这样可以做到 \(O(nq)\) 的复杂度。

    上面做法的本质是在一个 \(\tt DAG\) 上搜第 \(k\) 小。考虑倍增加速,首先要对每个节点确定一个后继节点。这个后继节点可以选择子串数最多的后继,有一个很好的性质是:如果不走到这个后继,那么当前节点的子串数折半

    既然已经确定了唯一的后继,我们可以直接倍增。如果倍增不动了就用暴力的方法跳一步,时间复杂度 \(O(q\log^2 n)\)虽然有更为优秀的做法,但是这种做法已经可以卡过去了

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 400005;
    #define ll long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,q,cnt=1,lt[M],fa[M],fc[M];
    ll sz[M],h[M][26],f[M][20],g[M][20];
    int nx[M][20],z[M][20];vector<int> e[M];
    struct node{int fa,len,ch[26];}a[M];
    int add(int p,int c)
    {
    	int np=++cnt;
    	a[np].len=a[p].len+1;
    	for(;p && !a[p].ch[c];p=a[p].fa)
    		a[p].ch[c]=np;
    	if(!p) a[np].fa=1;
    	else
    	{
    		int q=a[p].ch[c];
    		if(a[p].len+1==a[q].len) a[np].fa=q;
    		else
    		{
    			int nq=++cnt;a[nq]=a[q];
    			a[nq].len=a[p].len+1;
    			a[q].fa=a[np].fa=nq;
    			for(;p && a[p].ch[c]==q;p=a[p].fa)
    				a[p].ch[c]=nq;
    		}
    	}
    	return np;
    }
    void build()
    {
        queue<int> q;lt[1]=1;
        for(int v:e[1]) q.push(v);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            lt[u]=add(lt[fa[u]],fc[u]);
            for(int v:e[u]) q.push(v);
        }
    }
    void dfs(int u)
    {
    	if(!u || sz[u]) return ;
    	sz[u]=f[u][0]=1;int son=0,c=0;
    	for(int i=0;i<26;i++)
    	{
    		int v=a[u].ch[i];dfs(v);
    		sz[u]+=sz[v];h[u][i]=sz[u]-1;
    		if(sz[v]>sz[son]) son=v,c=i;
    	}
    	nx[u][0]=son;z[u][0]=c+'a';
    	for(int i=0;i<c;i++) f[u][0]+=sz[a[u].ch[i]];
    	g[u][0]=f[u][0]+sz[son];
    	for(int i=1;i<=18;i++)
    	{
    		nx[u][i]=nx[nx[u][i-1]][i-1];
    		z[u][i]=z[u][i-1]+z[nx[u][i-1]][i-1];
    		f[u][i]=f[u][i-1]+f[nx[u][i-1]][i-1];
    		g[u][i]=f[u][i-1]+g[nx[u][i-1]][i-1];
    	}
    }
    signed main()
    {
    	freopen("hymn.in","r",stdin);
    	freopen("hymn.out","w",stdout);
    	n=read();q=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		char s[5];scanf("%s",s);
    		fa[v]=u;fc[v]=s[0]-'a';
    		e[u].push_back(v);
    	}
    	build();dfs(1);
    	while(q--)
    	{
    		ll k;scanf("%lld",&k);
    		if(k>=sz[1]) {puts("-1");continue;}
    		int x=1,ans=0;k++;// 1 contains empty string
    		while(k>1)
    		{
    			for(int i=18;i>=0;i--) if(nx[x][i])
    				if(k>f[x][i] && k<=g[x][i])
    					ans+=z[x][i],k-=f[x][i],x=nx[x][i];
    			if(k==1) break;k--;
    			int t=lower_bound(h[x],h[x]+26,k)-h[x];
    			if(t>0) k-=h[x][t-1];
    			x=a[x].ch[t];ans+=t+'a';
    		}
    		printf("%d\n",ans);
    	}
    }
    
  • 相关阅读:
    批处理(bat)的一些记录
    在 Docker 中已运行的 container 如何修改 run 时的 env
    Linux 的一些命令记录
    Javascript aop(面向切面编程)之around(环绕)
    dojo Provider(script、xhr、iframe)源码解析
    dojo/request模块整体架构解析
    require、module、exports dojo中的三个特殊模块标识
    CSS垂直居中总结
    Javascript图片裁切
    CSS Font知识整理总结
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16484442.html
Copyright © 2020-2023  润新知