ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022牛客暑假第三场C、A、J、H、F题

2022-08-08 21:05:47  阅读:306  来源: 互联网

标签:node cnt return int ++ len 牛客 2022 第三场


咕咕咕了好久,因为H题去学了后缀自动机,顺手学了后缀数组,学了好久(其实主要还是因为懒)

C-Concatenation_"蔚来杯"2022牛客暑期多校训练营3 (nowcoder.com)

C题本意不是签到题,也刻意卡了log算法,但是卡得不够彻底,sort的cmp加个引用就过了。

数据量特别大,而且全是string,不可能把log卡得太死,猜也应该能莽掉。

正解trie树+扩展kmp(不会),想办法优化排序的比较。

莽过CODE:

bool cmp(string &a, string &b)
{
	string res1 = a + b;
	string res2 = b + a;
	if(res1 < res2) return true;
	else return false;
}

string ss[2000100];
int n;

int main()
{
	cin>>n;
	for(int i=0; i<n; i++) cin>>ss[i];
		
	sort(ss, ss+n, cmp);
	
	for(int i=0; i<n; i++) cout<<ss[i];
    return 0;
}

A-Ancestor_"蔚来杯"2022牛客暑期多校训练营3 (nowcoder.com)

这是第一道在赛场写出来的比较长代码(150行)的题目。

方法一:分类讨论,在比赛时上想出来的(其实是最笨的方法了感觉,害容易漏情况)

方法二:前后缀LCA

方法三:按照dfs序编号后,只有序相差最大的两个点会影响到LCA

分类讨论法CODE:

分类讨论注意到会改变lca的情况非常少,对于A树和B树都至多只有一个点。

因此思路是先判断是否去掉一个的点会使lca变化,如果会变化则搜索找到这个点,最后枚举删点的时候特判就可以了。

以下就是分类的方法。(图片来自网络,侵删)

const int N=1e5+5,M=2e5+5;
int e[M],ne[M];
int ah[N],bh[N],idx;
int n,k;
int x[N],inx[N];
int a[N],b[N];
int adep[N],bdep[N],fa[18][N],fb[18][N];
int anum[N],bnum[N];

void adde(int h[],int x,int y){
	e[idx]=y; ne[idx]=h[x]; h[x]=idx++;
}

void bfs(int h[],int dep[],int f[][N]){
	queue<int>q;
	dep[0]=0; dep[1]=1; f[0][1]=0;
	q.push(1);
	
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=h[u];~i;i=ne[i]){
			int v=e[i];
			if(dep[v]>dep[u]+1){
				dep[v]=dep[u]+1;
				f[0][v]=u;
				for(int j=1;j<=17;j++)f[j][v]=f[j-1][f[j-1][v]];
				q.push(v);
			}
		}
	}
}

int dfs(int h[],int num[],int u,int fa){
	num[u]=(inx[u]==1);
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		num[u]+=dfs(h,num,v,u);
	}
	return num[u];
}

int lca(int dep[],int f[][N],int a,int b){
	if(dep[a]<dep[b])swap(a,b);
	for(int j=17;j>=0;j--){
		if(dep[f[j][a]]>=dep[b])a=f[j][a];
	}
	if(a==b)return a;
	
	for(int j=17;j>=0;j--){
		if(f[j][a]==f[j][b])continue;
		a=f[j][a];
		b=f[j][b];
	}
	return f[0][a];
}

int dfs_find(int h[],int num[],int u,int fa){
	if(inx[u]==1)return u;
	
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		int tt=dfs_find(h,num,v,u);
		if(tt!=0)return tt;
	}
	return 0;
}

int find_point(int h[],int num[],int u){
	int cnt=0;
	for(int i=h[u];~i;i=ne[i]){
		if(num[e[i]])cnt++;
	}
	if(cnt>2)return -1;

	int dv=-1;
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		if(num[v]==1)dv=v;
	}

	if(dv==-1)return -1;
	else return dfs_find(h,num,dv,u);
}

int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++){
		scanf("%d",&x[i]);
		inx[x[i]]=1;
	}
	
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int tt;
	memset(ah,-1,sizeof(ah));
	for(int i=1;i<n;i++){
		scanf("%d",&tt);
		adde(ah,tt,i+1);
	}
	
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	memset(bh,-1,sizeof(bh));
	for(int i=1;i<n;i++){
		scanf("%d",&tt);
		adde(bh,tt,i+1);
	}
	
	memset(adep,0x3f,sizeof(adep));
	memset(bdep,0x3f,sizeof(bdep));
	bfs(ah,adep,fa); bfs(bh,bdep,fb);
	dfs(ah,anum,1,0); dfs(bh,bnum,1,0);
	
	int alca=x[1]; int blca=x[1];
	for(int i=2;i<=k;i++){
		alca=lca(adep,fa,alca,x[i]);
		blca=lca(bdep,fb,blca,x[i]);
	}
	
	int ta,tb;
	if(inx[alca])ta=alca;
	else ta=find_point(ah,anum,alca);
	if(inx[blca])tb=blca;
	else tb=find_point(bh,bnum,blca);
	
	int ans=0;
	for(int i=1;i<=k;i++){
		int t1=-1,t2=-1;
		if(ta==x[i]){
			for(int j=1;j<=k;j++){
				if(i==j)continue;
				
				if(t1==-1)t1=x[j];
				else t1=lca(adep,fa,t1,x[j]);
			}
		}
		else t1=alca;
		if(tb==x[i]){
			for(int j=1;j<=k;j++){
				if(i==j)continue;
				
				if(t2==-1)t2=x[j];
				else t2=lca(bdep,fb,t2,x[j]);
			}			
		}
		else t2=blca;
		if(a[t1]>b[t2])ans++;
	}
	cout<<ans<<endl;
	return 0;
}

J-Journey_"蔚来杯"2022牛客暑期多校训练营3 (nowcoder.com)

这一题主要是建图有点麻烦,我们把每条路径看成一个点,用二元组\((x,i)\)来表示一条路径,其中x是十字路口,i是指向十字路口的方向,和题目输入对应。

using namespace std;

const int N=5e5+5,INF=0x3f3f3f3f;
typedef pair<int,int>PII;
typedef long long ll;
int n;
int node[N][4];
int dis[N][4],vis[N][4];
PII st,ed;

void _01bfs(PII st,PII ed){
	memset(dis,0x3f,sizeof(dis));
	deque<PII>q;
	q.push_front(st); dis[st.x][st.y]=0;
	
	while(!q.empty()){
		PII u=q.front();
		q.pop_front();
		
		if(vis[u.x][u.y])continue;
		vis[u.x][u.y]=1;
		
		for(int i=0;i<4;i++){//遍历的写法稍微有一点麻烦
			PII v;
			v.x=node[u.x][i]; int val;
			
			if(v.x==0)continue;

			for(int j=0;j<4;j++){
				if(node[v.x][j]==u.x)v.y=j;
			}
			
			if(i==(u.y+1)%4)val=0;
			else val=1;
			
			if(dis[v.x][v.y]>dis[u.x][u.y]+val){
				dis[v.x][v.y]=dis[u.x][u.y]+val;
				if(val==0)q.push_front(v);//套01bfs即可
				else q.push_back(v);
			}
		}
	}
}

int main(){
	scanf("%d",&n);
	int a,b,c,d;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		node[i][0]=a; node[i][1]=b;
		node[i][2]=c; node[i][3]=d;
	}
	scanf("%d%d%d%d",&st.y,&st.x,&ed.y,&ed.x);
    //把起点和终点转化成我们需要的形式
	for(int i=0;i<4;i++)if(node[st.x][i]==st.y){ st.y=i; break;}
	for(int i=0;i<4;i++)if(node[ed.x][i]==ed.y){ ed.y=i; break;}
	
	_01bfs(st,ed);
	
	if(dis[ed.x][ed.y]!=INF)cout<<dis[ed.x][ed.y]<<endl;
	else cout<<-1<<endl;
	return 0;
}

H-Hacker_"蔚来杯"2022牛客暑期多校训练营3 (nowcoder.com)

后缀自动机缝合线段树。

假设母串A和需要匹配的B串,问题可以转化成:求出\(B_i\)为后缀的与A能匹配最长子串的长度\(len\),然后求出\([i-len+1,i]\)这个区间的最大字段和。

问题的前一个部分可以用后缀自动机来求,后一个部分用线段树预处理。

const int N=1e5+5;
typedef long long ll;
typedef struct Segtree{
	int l,r;
	ll sum,ssm,ls,rs;
}Seg;
Seg tr[4*N];
int las=1,tot=1;
struct SAM{
	int fa,len;
	int ch[26];
}node[2*N];
int n,m,k;
char s[N];
int w[N];

void extend(int c){
	int p=las, np=las=++tot;
	node[np].len=node[p].len+1;
	for(;p && !node[p].ch[c];p=node[p].fa)node[p].ch[c]=np;
	if(!p)node[np].fa=1;
	else{
		int q=node[p].ch[c];
		if(node[q].len==node[p].len+1)node[np].fa=q;
		else{
			int nq=++tot;
			node[nq]=node[q], node[nq].len=node[p].len+1;
			node[q].fa=node[np].fa=nq;
			for(;p && node[p].ch[c]==q; p=node[p].fa)node[p].ch[c]=nq;
		}
	}
}

void pushup(Seg &u,Seg &l,Seg &r){
	u.sum=l.sum+r.sum;
	u.ssm=max(l.rs+r.ls,max(l.ssm,r.ssm));
	u.ls=max(l.ls,l.sum+r.ls);
	u.rs=max(r.rs,r.sum+l.rs);
}

void pushup(int u){
	pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}

void build(int u,int l,int r){
	if(l>=r)tr[u]={l,r,w[l],w[l],w[l],w[l]};
	else{
		tr[u]={l,r};
		int mid=(l+r)>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
		pushup(u);
	}
}

Seg query(int u,int l,int r){
	if(tr[u].l>=l && tr[u].r<=r)return tr[u];
	int mid=(tr[u].l+tr[u].r)>>1;
	if(r<=mid)return query(u<<1,l,r);
	else if(l>mid)return query(u<<1|1,l,r);
	else{
		Seg lft=query(u<<1,l,r),rit=query(u<<1|1,l,r),sum;
		pushup(sum,lft,rit);
		return sum;
	}
}

int main(){
	scanf("%d%d%d",&n,&m,&k);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)extend(s[i]-'a');
	for(int i=1;i<=m;i++)scanf("%d",&w[i]);
	build(1,1,m);

	ll ans;
	while(k--){
		ans=0;
		scanf("%s",s+1);
		int p=1, len=0;
		for(int i=1;s[i];i++){
			int c=s[i]-'a';
			while(p>1 && !node[p].ch[c])p=node[p].fa, len=node[p].len;
			if(node[p].ch[c])p=node[p].ch[c], len++;
			if(len>0)ans=max(ans,query(1,i-len+1,i).ssm);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

F-Fief_"蔚来杯"2022牛客暑期多校训练营3 (nowcoder.com)

点双连通分量+结论题。

一补题才发现自己根本没有学明白点双....

题目分析:如果原图不连通,一定不能满足条件;如果图只有一条链,那么只有选择两端的城市才可以满足条件;如果图是一棵树,无论怎么选择都没办法满足条件;如果图是孤立的环(只没有链延申出去的,可以是几个共用点大于1的环合在一起),无论怎么选择都可以满足条件。

于是可以想到用V-DCC来把环缩掉,最后判断图是否是一条链,如果是链,再对输入判断是否位于链的两个端点即可。

const int N=2e5+5,M=4e5+5;
int e[M],ne[M];
int h[N],idx;
int n,m,q;
int dfn[N],low[N],timestamp,root;
int stk[N],cut[N],dcc_cnt,top,num;
vector<int>dcc[N],to[N];
int d[N],id[N];
int block_cnt,link,cut_cnt;

void adde(int x,int y){
	e[idx]=y; ne[idx]=h[x]; h[x]=idx++;
}

void vdcc(int u){
	dfn[u]=low[u]=++timestamp;
	stk[++top]=u;
	
	if(u==root && h[u]==-1){
		dcc_cnt++;
		dcc[dcc_cnt].push_back(u);
		return;
	}
	
	int cnt=0;
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		if(!dfn[v]){
			vdcc(v);
			low[u]=min(low[u],low[v]);
			
			if(dfn[u]<=low[v]){
				cnt++;
				if(u!=root || cnt>1){
					cut_cnt++;//记录割点数量,如果割点数量为0说明是孤立环
					cut[u]=1;
				}
				
				dcc_cnt++;
				int y;
				do{
					y=stk[top--];
					dcc[dcc_cnt].push_back(y);
				}while(y!=v);
				dcc[dcc_cnt].push_back(u);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

int main(){
	scanf("%d%d",&n,&m);
	int x,y;
	memset(h,-1,sizeof(h));
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		adde(x,y); adde(y,x);
	}
	
	for(root=1;root<=n;root++){
		if(!dfn[root]){
			vdcc(root);
			block_cnt++;//判断连通性的
		}
	}
	
	num=dcc_cnt; link=1;
	for(int i=1;i<=dcc_cnt;i++){
		for(int u:dcc[i]){
			if(cut[u]){
				if(!id[u])id[u]=++num;//割点需要重新编号
				to[id[u]].push_back(i);//给缩点后的新图建立边
				to[i].push_back(id[u]);
				d[id[u]]++; d[i]++;//记录节点度数
				if(d[id[u]]>=3 || d[i]>=3){ link=0; break;}
			}
			else id[u]=i;
		}
	}
	
	scanf("%d",&q);
	while(q--){
		scanf("%d%d",&x,&y);
		if(block_cnt>1 || link==0)printf("NO\n");//不连通或者不是链
		else if(cut_cnt==0)printf("YES\n");//孤立环
		else if(d[id[x]]==1 && d[id[y]]==1 && id[x]!=id[y])printf("YES\n");//满足条件
		else printf("NO\n");
	}
	return 0;
}

标签:node,cnt,return,int,++,len,牛客,2022,第三场
来源: https://www.cnblogs.com/tshaaa/p/16563374.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有