2022.10.5测试

一测:?(由于数据坏了)

T1:预计蓝(2000)

T2:预计紫(2700)

T3:预计蓝(2200)


1.简单点
题目背景
今天有个巨佬不讲题德,出了个题,说他是乱出的水题。
他出的可不是水题啊,Trie树,后缀树,AC自动机,训练有素;后来听说他打了三年NOI,看来是,
有备而来。
题目描述
有一个非常 easy 的字符串 , 由四种字符 e , a , s , y 组成。 的下标从1开始。
接下来有 组询问,每组询问会给出一个区间 ,代表 的子串 。询问你这个子串的简单程
度。
一个字符串 的简单程度是如下定义的:
如果 存在这样的子序列:该序列由"easy"反复出现 次拼接而成。那么, 中所有此满足条件的子
序列的最大 值就是 的简单程度。
例如:字符串"eeaasey"的简单程度就是1,"eaeasyeasyea"的简单程度是2。
对于字符串的子序列的定义:是从字符串中选择任意字符,在不改变相对位置的情况下拼接而得到的
序列。
输入格式
第一行,一个字符串 。
第一行,一个正整数 。
接下来 行,每行两个正整数 ,代表询问的子串。
输出格式
对于每组询问,输出子串的简单程度。
样例 #1
样例输入 #1
easy
3
1 4
2 4
1 3
样例输出 #1
样例 #2
样例输入 #2
样例输出 #2
提示
【数据范围】
对于25%的数据,满足
对于70%的数据,满足
对于100%的数据,满足 S<=|10^5|,m<=3*10^5
时间限制1.00s
内存限制512.00MB

T1:

对于每一个 \(e\) 找到离的最近的一个 \(a\)

对于每一个 \(a\) 找到离的最近的一个 \(s\)

对于每一个 \(s\) 找到离的最近的一个 \(y\)

对于每一个 \(y\) 找到离的最近的一个 \(e\)

这样我们可以对上述操作进行连边操作,就可以建出一个 DAG。

对于区间 \((l,r)\),找到离 \(l\) 最近的一个 \(e\),随着 \(e\) 往后跑,记录跑的步数后除以 \(4\) 统计答案。

跑的过程可以利用倍增优化。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=3e5+5;
char s[N];
int n,m,k,p[N],t[N][4],res[N],f[N][25],tot,tim;
bool vis[N];
int main()
{
	//freopen("easier1.in","r",stdin);
	//freopen("a.out","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=0;i<4;i++)t[n][i]=n+1,t[n+1][i]=n+1;
	for(int i=n-1;i>=0;i--)
	{
		for(int j=0;j<4;j++)t[i][j]=t[i+1][j];
		if(s[i+1]=='e')t[i][0]=i+1;
		if(s[i+1]=='a')t[i][1]=i+1;
		if(s[i+1]=='s')t[i][2]=i+1;
		if(s[i+1]=='y')t[i][3]=i+1;
	}
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='e')f[i][0]=t[i][1];
		if(s[i]=='a')f[i][0]=t[i][2];
		if(s[i]=='s')f[i][0]=t[i][3];
		if(s[i]=='y')f[i][0]=t[i][0];
	}
	f[n+1][0]=n+1;
	for(int j=1;j<=20;j++)
	{
		for(int i=1;i<=n+1;i++)
		{
			f[i][j]=f[f[i][j-1]][j-1];
		}
	}
	scanf("%d",&m);
	k=sqrt(n);
	for(int i=1;i<=n;i++)p[i]=i/k;
	for(int i=1;i<=m;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		int x=t[l-1][0];
		int num=1;
		for(int j=20;j>=0;j--)if(f[x][j]<=r)x=f[x][j],num+=(1<<j);
		printf("%d\n",num/4);
	}
	return 0;
}

2.树上异或
题目背景
有一棵含有 个结点的树,树上每个点有一个非负整数权值 。
题目描述
但是 忘记树上每个点的权值了,他只记得第 个结点的 在一个区间 范围内。
此外,他还记得每树上每对相邻结点的异或值,并记在了这对结点的边上。
现在,他想知道,这棵树有多少种不同的 序列能满足上述限制。
1
0
0
eeaseyaesasyy
4
1 13
2 12
2 10
3 11
2
2
1
0
输入格式
第一行,一个正整数 。
接下来 行,第 行有两个整数 。
接下来 一行,每行三个整数 。表示在树上的点 之间有一条边,以及 点
上的权值的异或值。
输出格式
输出一行,一个整数,表示答案。
样例 #1
样例输入 #1
样例输出 #1
提示
对 10% 的数据,满足
对 30% 的数据,满足
另有 20% 的数据,满足 是 的整数次幂
对 100% 的数据,满足 n<=10^5,所有数及运算过程在 int 以内。
时间限制2.00s
内存限制256.00MB

T2:

对于这棵树只要确定了一个点,其他点都可以根据树边推出其他所有点。

并且对于一个合法的树,在树上每个点都对一个点取异或一个数 \(x\),树依然合法。

证明:对于两个点都异或了一个数,由于 \(x\oplus x=0\),所以异或值不变,树依然合法。

那么我们可以先将根赋为 \(0\),得到一个特殊解,那么问题就转换成了对一堆数异或一个值使其在规定的范围中。

由于共 \((r-l+1)\) 个数与原数异或可以得到对应的 \(l\)\(r\) 中的一个值。

可以枚举 \(l\)\(r\) 找出那些值,进行累加,看哪些值可以符合 \(n\) 个点即可。

复杂度极高,考虑优化。

对于前 \(k\) 位二进制对应的数相同,后面的 \(t\) 位二进制对应的数由全部为 \(0\) 到 全部为 \(1\) 的一个区间(如 \(1000\)\(1111\)),这个区间异或上同一个数,得到的答案仍然是个连续的区间。

证明:由于前 \(k\) 位数,由于相同,无论怎么异或不会改变,而后面的数占满了 \(2^t\),所以依然连续。

很明显用字典树,但用字典树仍然会 T。

考虑分治,对于分治区间 \((l_2,r_2)\) 与所求区间 \((l,r)\),若 \(mid=(l_2+r_2)\div2>l_2\),分治求左区间,若 \(mid<=r_2\),分治求右区间,类似线段树。(这地方的确不太好想,但自己画画应该就出来了,建议自己举个例理解下)

处理出数个区间后利用差分维护,最后找等于 \(n\) 的数个数即可。

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#define int long long
using namespace std;
const int N=5e5+5;
int n,num[N],cnt,nl,nr,cnp,kt;
struct node
{
	int l,r;
}t[N];
struct node2
{
	int to,data;
};
vector<node2>a[N];
struct node3
{
	int l,r;
}p[10*N];
map<int,int>m;
void dfs(int x,int fa)
{
	int len=a[x].size();
	for(int i=0;i<len;i++)
	{
		if(a[x][i].to==fa)continue;
		num[a[x][i].to]=num[x]^a[x][i].data;
		dfs(a[x][i].to,x);
	}
}
void get_ans(int l,int r)
{
	if(l>=nl&&r<=nr)
	{
		int len=r-l+1;
		int tl=l^(kt&(~(len-1))),tr=tl+len-1;
		if(tl>tr)swap(tl,tr);
		p[++cnp]={tl,tr+1};
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=nl)get_ans(l,mid);
	if(mid<nr)get_ans(mid+1,r);
}
signed main()
{
	//freopen("xortree6.in","r",stdin);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&t[i].l,&t[i].r);
	for(int i=1;i<n;i++)
	{
		int u,v,w;
		scanf("%lld%lld%lld",&u,&v,&w);
		a[u].push_back({v,w});
		a[v].push_back({u,w});
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)nl=t[i].l,nr=t[i].r,kt=num[i],get_ans(0,(1<<30)-1);
	for(int i=1;i<=cnp;i++)
	{
		m[p[i].l]++;
		m[p[i].r]--;
	}
	int num=0,ans=0,bef=-1;
	for(auto i:m)
	{
		if(num==n)ans+=i.first-bef;
		num+=i.second;
		bef=i.first;
	}
	printf("%lld",ans); 
	return 0;
}

3. 循环同构串
题目背景
因为题目简单,同学们开开心心的研究起了字符串的性质。
题目描述
4
0 7
1 6
2 5
3 4
1 2 0
1 3 7
2 4 6
2
如果将字符串 的所有字符左移一位,然后让第一位字符移动到最后一位,即构成新的
字符串: 。这个操作称为字符串的左移。
比如字符串 左移一次得到字符串 。
若 串能够通过若干次左移之后变成 串,我们就称 和 循环同构,
现在给你一个长度为 的字符串 ,询问字符串 是否满足以下条件:
1. 存在一个整数 ,满足 是 的因数。
2. 将 分成 段子串,每段等长,长度为 。
3. 存在一个字符串 ,与上述的 段子串都循环同构。
如果存在 满足上述三个条件,那么就输出 Yes ,否则输出 No 。
输入格式
一行,一个整数 ,表示有 组数据。
对于每组数据,输入两行:
第一行,一个整数 ,表示字符串长度。
第二行,一个长度为 的字符串。
输出格式
对于每组数据,输出对应的 Yes 或者 No
样例 #1
样例输入 #1
样例输出 #1
6
1
a
2
aa
3
aab
4
abba
6
abcbcc
8
aaaaaaaa
提示
【数据范围】:
对20%的数据满足,
对40%的数据满足,
对100%的数据满足,n<=5*10^6,t<=20
字符串仅由小写字母组成。
保证单个输入文件的大小不超过4Mb
数据可能还有更细致的梯度。
时间限制2.00s
内存限制512.00MB

枚举区间长度 \(len\),对于每一次,找到第一块区间,将它复制成两块,求其中所有长度为 \(len\) 的哈希值,存储至 map 中,然后判断后面的每一块区间的哈希值是否出现在 map 中过即可。

数据过水,正解忘了,反正开 O2 能过(理直气壮)。

#include<iostream>
#include<cstdio>
#include<map>
#define int long long
using namespace std;
const int N=5e6+5;
const int mod=1e9+7;
char s[N];
int n,hasht[N],power[N];
map<int,bool>m;
void clac(int len)
{
	m.clear();
	for(int i=1;i<=len;i++)
	{
		hasht[i]=27*hasht[i-1]+(s[i]-'a'+1);
		hasht[i]%=mod;
	}
	for(int i=1;i<=len;i++)
	{
		hasht[i+len]=27*hasht[i+len-1]+(s[i]-'a'+1);
		hasht[i+len]%=mod;
	}
	for(int i=0;i<=len;i++)m[hasht[i+len]-hasht[i]*power[len]]=1;
}
bool check(int len)
{
	for(int i=1;i<n/len;i++)
	{
		int num=0;
		for(int j=i*len+1;j<=(i+1)*len;j++)num=num*27+(s[j]-'a'+1),num%=mod;
		if(!m[num])return false;
	}
	return true;
}
signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	int T;
	scanf("%lld",&T);
	power[0]=1;
	for(int i=1;i<=N-5;i++)power[i]=power[i-1]*27,power[i]%=mod;
	while(T--)
	{
		scanf("%lld",&n);
		scanf("%s",s+1);
		bool flag=0;
		for(int i=2;i<=n;i++)
		{
			if(n%i==0)
			{
				clac(n/i);
				if(check(n/i))
				{
					flag=1;
					break;
				}
			}
		}
		if(flag)printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}
posted @ 2023-02-24 13:56  Gmt丶Fu9ture  阅读(25)  评论(0)    收藏  举报