这次应该叫高二上一调

挺神奇的说。

T1:匹配

题意:给定一个字符串和一个该字符串的子串,并在子串之后再添加一个字符,求修改后子串的后缀与原字符串的前缀的最大匹配长度。

思路:Hash或者KMP均可。考场上对着Hash冲(调)了半个多小时,发现进位进错了...

代码:

#include <bits/stdc++.h>
#define ull unsigned long long
#define Reg register
using namespace std;
const int maxn=300010;
int n,m,t,la,lb;
char K1[maxn];
ull p[maxn],f1[maxn],f2[maxn];
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
inline void pre(){
	p[0]=1;
	for(Reg int i=1;i<=200010;++i) p[i]=p[i-1]*131;
}
inline void solve(){
	la=read();lb=read();
	lb++;
	char opt;
	//注意特判la=lb的情况 
	scanf("%s",K1+1);
	scanf(" %c",&opt);
	for(Reg int i=1;i<=lb;++i){
		f1[i]=f1[i-1]*131+K1[i];
		if(i==lb){
			f2[i]=f2[i-1]*131+opt; 
                        //麻
			break;
		} 
		f2[i]=f1[i];
	}
	int maxlen=min(la,lb);
	for(Reg int len=maxlen;len>=1;--len){
		//printf("%lld %lld\n",f2[lb-len],p[len]);
		//printf("%lld %lld\n",f2[lb]-f2[lb-len]*p[len],f1[len]);
		if(f2[lb]-f2[lb-len]*p[len]==f1[len]){
			printf("%d\n",len);
			return;
		}
	}
	printf("0\n");
	return;
}
int main(){
	t=read();pre();
	while(t--) solve();
	return 0;
}
/*
3
5 3
adabc
d
14 14
abcdefghijklmn
a
6 6
aaaaaa
a
*/

T2:回家

题意:给定一个有\(n\)个节点,\(m\)条边的图,求\(1\)\(n\)路径的必经点(除起点与终点)。

思路:想到了\(tarjan\),但没有完全想到。咱甚至以为还是跟之前的某一神必题(电 压 寄 制)一样要对\(tarjan\)进行魔改,所以没往这方面仔细想。于是打了\(30pts\)的暴力和\(10pts\)的树形结构特判。

然后考完一看题解就麻掉了。

实际上可以用\(tarjan\)求出所有的割点,由于必经点一定是割点,而割点不一定是必经点,所以要判断该点是否是割点的同时还要判断它的子节点是否能到达\(n\)

至于为什么要判断子节点而不是自己:

  • 首先如果它的子节点能到达\(n\),那么它自己一定可以到达\(n\)

  • 其次如果说该点对某一搜索树不是割点却能够抵达\(n\),自己会带上标记,等到遍历完下一个搜索树且搜索树中不包含\(n点\)但是满足割点条件时,若对自己进行判断,则会使答案错误地增加\(1\),而对子节点判断不会出现这种情况。

(虽说直接判断自己也能得个\(95pts\)吧)

代码:

#include <bits/stdc++.h>
#define ull unsigned long long
#define Reg register
using namespace std;
const int maxn=205010;
int n,m,t,tot,cnt;
int sl[maxn],dfn[maxn],low[maxn],visn[maxn];
bool vis[maxn];
vector<int> e[maxn];
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
inline void pre(){
	tot=0;cnt=0;
	memset(vis,0,sizeof(bool)*(n+10));
	memset(visn,0,sizeof(int)*(n+10));
	memset(dfn,0,sizeof(int)*(n+10));
	memset(low,0,sizeof(int)*(n+10));
	for(Reg int i=1;i<=n;++i) e[i].clear();
}
inline void add(int u,int v){
	e[u].push_back(v);
}
inline void tarjan(int u,int fa){
	dfn[u]=low[u]=++cnt;
	bool K=0;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==fa)continue;
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			visn[u]=max(visn[u],visn[v]);
			if(dfn[u]<=low[v]&&visn[v]&&u!=1&&u!=n&&!K) K=1,sl[++tot]=u;
			//注意是v不是u
		}else{
			low[u]=min(low[u],dfn[v]);
		}
	}
}
inline void solve(){
	visn[n]=1;
	for(int i=1;i<=n;++i){
		if(!dfn[i]) tarjan(i,0);
	}
	sort(sl+1,sl+1+tot);
	printf("%d\n",tot);
	for(int i=1;i<=tot;++i) printf("%d ",sl[i]);
	printf("\n");
	return;
}
int main(){
	t=read();
	while(t--){
		n=read();m=read();pre();
		for(int i=1;i<=m;++i){
			int x=read(),y=read();
			if(x==y) continue;
			add(x,y);add(y,x);
		}
		solve();
	}
	return 0;
}
/*
1
8 7
1 2
1 3
1 4
3 5
3 6
6 8
4 7
1
6 6
1 2
2 3
3 4
4 5
5 2
4 6
*/

T3:寿司

题意:给定一个只包含'R'和'B'的环状字符串,求将环改变至两种不同颜色的区域的最少步数,只能交换相邻的两个字符。

思路:什么神必题,字符串我rnm

不会。

听完\(Eafoo\)巨—————佬讲完他的做法之后感觉好像有点明白了,想找个人问一问式子的时候被好心的\(Starlight\)捉去(不是)弄懂了\(Sakula\)巨——————佬的做法。

根据归中的原理,我们知道每次把一个固定的字符形式'R'或'B'集中起来步数最少,而步数的求得实际上是坐标的相减,那么我们就可以处理出坐标前缀和来解决问题。

每一次更改之前查询\(mid\),然后利用等差数列求和计算出我们将某一侧的字符集中于一边时的坐标和,以左侧为例,再减去原来左侧的坐标和即使移动步数,右侧同理,这样一来就可以有\(O(n)\)的更改环,与\(O(1)\)的查询步数。在更改环的时候只需要将队列中的最左端移动到最右端再加上\(n\)即可,注意还要维护到左侧与右侧坐标和的更改。

由于某些显而易见的性质,奇数长度与偶数长度实际上是不一样的,我们需要分别进行处理。

然后就无了。

代码:

#include <bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define Reg register
using namespace std;
const int maxn=2005010;
int t,n,m,l,r,minn;
int q[maxn];
char K[maxn];
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
//先按这样来搞吧 
inline void workodd(){
	int sum1=0,sum2=0;
	for(int i=1;i<=(m/2);++i) sum1+=q[i];
	for(int i=(m+1)/2+1;i<=m;++i) sum2+=q[i];
	for(int i=1;i<n;++i){
		int mid=(l+r)>>1;
		int lefx=q[mid]*(m/2)-(m/2+1)*(m/2)/2;
		int rigx=q[mid]*(m/2)+(m/2+1)*(m/2)/2;
		minn=min(minn,lefx-sum1+sum2-rigx);
		sum1+=q[mid]-q[l];
		sum2+=q[l]+n-q[mid+1];
		q[++r]=q[l]+n;
		l++; 
	}
}
inline void workowen(){
	int sum1=0,sum2=0;
	for(int i=1;i<=m/2-1;++i) sum1+=q[i];
	for(int i=m/2+1;i<=m;++i) sum2+=q[i];
	for(int i=1;i<n;++i){
		int mid=(l+r)>>1;
		int lefx=q[mid]*(m/2-1)-(m/2)*(m/2-1)/2;
		int rigx=q[mid]*(m/2)+(m/2+1)*(m/2)/2;
		minn=min(minn,lefx-sum1+sum2-rigx);
		sum1+=q[mid]-q[l];
		sum2+=q[l]+n-q[mid+1];
		q[++r]=q[l]+n;
		l++; 
	}
}
inline void solve(){
	minn=999999999999;r=0;l=1;
	scanf("%s",K+1);
	n=strlen(K+1);
	for(int i=1;i<=n;++i){
		if(K[i]=='B') q[++r]=i;
	}
	m=r;
	if(m&1) workodd();
	else workowen(); 
	printf("%lld\n",minn);
}
signed main(){
	t=read();
	while(t--) solve();
	return 0;
}
/*
1
BBRBBRBBBRRR
*/

数奥的明天集训,不用考一调了,麻。

posted @ 2022-07-09 09:37  Broken_Eclipse  阅读(37)  评论(0编辑  收藏  举报

Loading