ZJC比赛

\(\large{Updatete}\)

先放张图 😇😇😇

这次是真的没想再改了,但是一到教室就又会怎么优化了
没必要每个点都让它和其他所有点判断一下,可以从上一个点加加减减啥的转过来

然后我就在昨天那个 \(n*n\) 代码的基础上,只让它把第一个点的值求出来,剩下用优化的思想从上一个点转移再加加减减确定最优值

点击查看答辩
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=1e8;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",c);
		len=strlen(c);
		for(int i=len;i>=1;i--)
			c[i]=c[i-1];
		tb=c[0]=0;
		for(int i=1;i<=len;i++)
			if(c[i]=='R')
				b[++tb]=i;
		for(int i=tb+1;i<=tb*2;i++)
			b[i]=b[i-tb]+len;
		for(int i=1;i<=tb*2;i++)
			q[i]=q[i-1]+b[i]-b[i-1]-1;
		int res=0,cnt=0,zz=1;
		while((cnt<res&&zz+1<=1+tb)||!res)
		{
			zz++;
			if(cnt)
				res=cnt;
			cnt=0;
			for(int j=2;j<zz;j++)
				cnt+=q[j]-q[1];
			for(int j=zz;j<1+tb;j++)
				cnt+=q[1+tb]-q[j];
		}
		zz--;
		ans=res;
		int l=zz,r=tb-zz;
		for(int i=2;i<=tb;i++)
		{
			res+=r*(q[zz+r+1]-q[zz+r])-(l-1)*(q[zz-l+2]-q[zz-l+1]);
			l--,r++;
			while(res-q[zz+r]+q[zz+1]+q[zz+1]-q[zz-l+1]<=res)
			{
				res=res-q[zz+r]+q[zz+1]+q[zz+1]-q[zz-l+1];
				zz++,l++,r--;
			}
			//cout<<ans<<" "<<res<<" "<<l<<" "<<r<<"---\n";
			ans=min(ans,res);
		}
		printf("%d\n",ans);
	}
	return 0;
}

发现一个点也过不了
再发现循环 i 次, i 一次也没用上,然后就看到其实 $zz-l+1==i $ $ zz+l=i+tb$
也就是 l,r 不仅啥也没优化到反而增加了思维复杂度 😅

但是并没有什么质的改变,所以还是过不了
再看到自己昨天写的代码以 zz 作为分割点,i~zz-1 是一部分,zz~i+tb 是一部分
但是今天写的东西是建立在 zz 属于第一部分上的

改完就过了,,,

点击查看答辩
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=1e8;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",c);
		len=strlen(c);
		for(int i=len;i>=1;i--)
			c[i]=c[i-1];
		tb=c[0]=0;
		for(int i=1;i<=len;i++)
			if(c[i]=='R')
				b[++tb]=i;
		for(int i=tb+1;i<=tb*2;i++)
			b[i]=b[i-tb]+len;
		q[1]=0;
		for(int i=2;i<=tb*2;i++)
		{
			q[i]=q[i-1]+b[i]-b[i-1]-1;
//			cout<<q[i]<<' ';
		}
//		cout<<endl;
		int res=0,cnt=0,zz=1;
		while((cnt<res&&zz<=tb)||!res)
		{
			zz++;
			if(cnt)
				res=cnt;
			cnt=0;
			for(int j=2;j<=zz;j++)
				cnt+=q[j]-q[1];
			for(int j=zz+1;j<1+tb;j++)
				cnt+=q[1+tb]-q[j];
		}
		zz--;
		ans=res;
//		cout<<ans<<" "<<res<<" "<<zz<<"---\n";
		for(int i=2;i<=tb;i++)
		{
			res+=(i+tb-zz-1)*(q[i+tb]-q[i+tb-1]);
			res-=(zz-i+1)*(q[i]-q[i-1]);
			while(res-q[i+tb]+q[zz+1]+q[zz+1]-q[i]<res&&zz<i+tb)
			{
				res=res-q[i+tb]+q[zz+1]+q[zz+1]-q[i];
				zz++;
			}
			ans=min(ans,res);
		}
		printf("%d\n",ans);
	}
	return 0;
}

又是就过了 \(50pts\)

莫名其妙,真是让人摸不着头发 😅

再再再再然后,发现没必要从第一个点的最优解开始转移
也就是说就不该留昨天的代码,哪怕只对一个点求也会 T 掉

删了就对了
,当然把最初始的状态加上,也不用管他是不是最优的 总不能转移空气吧

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+10;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
signed main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",c);
		len=strlen(c);
		for(int i=len;i>=1;i--)
			c[i]=c[i-1];
		tb=c[0]=0;
		for(int i=1;i<=len;i++)
			if(c[i]=='R')
				b[++tb]=i;
		for(int i=tb+1;i<=tb*2;i++)
			b[i]=b[i-tb]+len;
		q[1]=0;
		for(int i=2;i<=tb*2;i++)
			q[i]=q[i-1]+b[i]-b[i-1]-1;
		int res=0,zz=1;
		for(int i=2;i<=tb;i++)
			res+=q[tb+1]-q[i];
		ans=res;
		for(int i=1;i<=tb;i++)
		{
			if(i!=1)
			{
				res+=(i+tb-zz-1)*(q[i+tb]-q[i+tb-1]);
				res-=(zz-i+1)*(q[i]-q[i-1]);
			}
			while(res-q[i+tb]+q[zz+1]+q[zz+1]-q[i]<res&&zz<i+tb)
			{
				res=res-q[i+tb]+q[zz+1]+q[zz+1]-q[i];
				zz++;
			}
			ans=min(ans,res);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

😇😇😇

小插曲
Huge:我觉得你们最近学习状态不错,找我调代码的都少了...

某人:老师有空帮忙看看我的代码呗

Huge:...调代码这件事吧,还得看你们自己,总不能考场上也让别人帮你调吧
(显然没有成功)

————————————————————————————————————————————————————————————————————————————————————————————————————————

\(\large{Uptated}\)

本来没有继续写t3的欲望了,但是昨天DZ说他改出来了,然后我就去再看了眼题解

当时想的是之前因为和题解思路不太一样,所以没仔细看,这回看到了关键字:二分、单调指针之类的
但是并不知道我的思路怎么二分

然后晚上回去想了5分钟就知道我赛时代码哪里假了
转化成链后,应该在两个点(相同的点)之间的剩下 \(n-1\) 个点上取离那两个点近的那一个

然后 二分||指针 的思路也就有了:
因为肯定是一边全向一边转,另一边相反,且下一个分割点貌似一定在这一个的右边
所以说 二分||指针 出那个分割点就好

顺便再想了会 Tarjan 的有向图,发现现在只会强连通分量了,想无向图想不出来刚打算睡觉的时候,被同学的呼噜轰炸了半个小时才睡着

今天本来上午最后一节公自就该来机房实现一下思路的,但是有俩山大的学长来宣传学校了,讲完已经上课了也没好意思再往外走

下午来机房后先花了3分钟改进了一下之前的代码,也就是对每一个点,只在链上同一个点中间计算离哪个近,发现能跑 \(50pts\) (记住这个数)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",c);
		len=strlen(c);
		for(int i=len;i>=1;i--)
			c[i]=c[i-1];
		tb=c[0]=0;
		for(int i=1;i<=len;i++)
			if(c[i]=='R')
				b[++tb]=i;
		for(int i=tb+1;i<=tb*2;i++)
			b[i]=b[i-tb]+len;
		for(int i=1;i<=tb*2;i++)
			q[i]=q[i-1]+b[i]-b[i-1]-1;
		ans=N<<5;
		for(int i=1;i<=tb;i++)
		{
			int res=0;
			for(int j=i+1;j<i+tb;j++)
				res+=min(q[j]-q[i],q[i+tb]-q[j]);
			ans=min(ans,res);
		}
		printf("%d\n",ans);
	}
	return 0;
}

然后就开始写优化
试了一下二分发现不太适合在这里用
然后就敲了一节课的单调指针,说实话真没想到能用这么久
反正最后过了,,,

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=1e9;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",c);
		len=strlen(c);
		for(int i=len;i>=1;i--)
			c[i]=c[i-1];
		tb=c[0]=0;
		for(int i=1;i<=len;i++)
			if(c[i]=='R')
				b[++tb]=i;
		for(int i=tb+1;i<=tb*2;i++)
			b[i]=b[i-tb]+len;
		for(int i=1;i<=tb*2;i++)
			q[i]=q[i-1]+b[i]-b[i-1]-1;
		ans=inf;
		int res,cnt,zz=1;
		for(int i=1;i<=tb;i++)
		{
			cnt=res=0;
			while((cnt<res&&zz+1<=i+tb)||!res)
			{
				zz++;
				if(cnt)
					res=cnt;
				cnt=0;
				for(int j=i+1;j<zz;j++)
					cnt+=q[j]-q[i];
				for(int j=zz;j<i+tb;j++)
					cnt+=q[i+tb]-q[j];
			}
			zz-=2;
			ans=min(ans,res);
		}
		printf("%d\n",ans);
	}
	return 0;
}

\(45pts\)

那正确性肯定没问题啊,问题出在哪呢
可以发现纠正完后的赛时代码时间复杂度是 \(n*n\)
但是我优化了一个单调指针之后,就成 \(n*n*log(n)\) 的了
最会优化的一集 😅

咋最近这么能浪费时间啊 😇😇😇

欸,好像想到咋优化了 (逃
事实证明并没有,哪位大佬知道我这思路咋优化,求教教

————————————————————————————————————————————————————————————————————————————————————————————————————————

昨天 \(Huge\) 说要给信奥的考场试,我以为我们仨不用考来着,一来机房,打开OJ,哦,ZJC比赛,妙啊,(话说为啥把我放在最后一个,虽然最后我考的也最烂就是了,下次一定要让他把我放在第一个

T1

题意:给一个字符串,第二个字符串是前一个字符串的前一半 + 一个字符,问 1 的前缀和 2 的后缀最多相等的个数

显然 \(KMP\) 板子题,,,但是我忘了
但是显然 \(hash\) 也能做,但是我忘了咋求字串的hash值了

那么怎么办呢

我把前一个字符串的所有可能情况都求了个hash值,甚至还没有直接一个一个比对跑得快
(因为答案最多就是第二个字符串的长度也就是第一个的一半)

,大概是因为kmp没打出来,然后急了
😅

赛时代码 64pts
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int base=13331;
unsigned long long a[N],b[N]; 
int T,la,lb;
char ca[N],cb[N];
inline void Hash()
{
	unsigned long long res;
	for(int i=1;i<=la;i++)
	{
		res=0;
		for(int j=i;j>=1;j--)
			res=res*base+ca[j];
		a[i]=res;
	}
	for(int i=1,j=lb;j>=1;i++,j--)
		b[i]=b[i-1]*base+cb[j];
	return ;
}
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%d%d",&la,&lb);
		scanf("%s",ca+1);
		for(int i=1;i<=lb;i++)
			cb[i]=ca[i];
		scanf("%c",&cb[++lb]);
		while(cb[lb]<'a'||cb[lb]>'z')
			scanf("%c",&cb[lb]);
		Hash();
		for(int i=lb;i>=0;i--)
		{
			if(a[i]==b[i])
			{
				printf("%d\n",i);
				break;
			}
		}
	}
	return 0;
}
赛后代码 100pts
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int base=13331;
unsigned long long a[N],b[N],p[N];
int T,la,lb;
char ca[N],cb[N];
inline void Hash()
{
	unsigned long long res;
	
	for(int i=1;i<=lb;i++)
	{
		a[i]=a[i-1]*base+ca[i];
		b[i]=b[i-1]*base+cb[i];
	}
	return ;
}
int main()
{
	p[0]=1;
	for(int i=1;i<=N;i++)
		p[i]=p[i-1]*base;
	cin>>T;
	while(T--)
	{
		scanf("%d%d",&la,&lb);
		scanf("%s",ca+1);
		for(int i=1;i<=lb;i++)
			cb[i]=ca[i];
		scanf("%c",&cb[++lb]);
		while(cb[lb]<'a'||cb[lb]>'z')
			scanf("%c",&cb[lb]);
		Hash();
		int f=1;
		for(int i=1,j=lb;i<=lb;i++,j--)
		{
			if(a[j]==b[lb]-b[i-1]*p[lb-i+1])
			{
				f=0;
				printf("%d\n",j);
				break;
			}
		}
		if(f)
			printf("0\n");
	}
	return 0;
}

/*
2
5 3
adabc
d
6 6
aaaaaa
a
*/

T2

题意:\(Tarjan\) 求割点的板子

老师预料到我还记得tarjan咋敲,但是他没想到我根本没写无向tarjan的题

不过好的一点是我前两天看了dij求最小环的做法(我为啥要看这?),大概有 \(30pts\) 的思路,就是把每条边删了,再从1 \(dfs\) 到n 看看走不走得通

然后就出现了一点问题:
图是双向的,但是我单向删的边,所以还没图保证为树的特殊性质分高
如果你还感觉不太对劲的话,大概是我脑子抽了不知道为啥求割点要删边去判,反例随便就能举出来

最后特殊性质也忘了打了 😅

赛时代码 5pts
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
int T,n,m,tot,cnt,res,ans[N];
int head[N],nxt[N<<1],to[N<<1];
bool flag[N],f;
inline void add(int u,int v)
{
	nxt[++tot]=head[u];
	head[u]=tot;
	to[tot]=v;
	return ;
}
inline void dfs(int x)
{
	flag[x]=true;
	if(x==n)
		f=1;
	if(f)
		return ;
	int y;
	for(int i=head[x];i;i=nxt[i])
	{
		y=to[i];
		if(!flag[y])
			dfs(y);
	}
	return ;
}
int main()
{
	cin>>T;
	while(T--)
	{
		for(int i=1;i<=n;i++)
			head[i]=0;
		scanf("%d%d",&n,&m);
		for(int i=1,u,v;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		int ne,t;
		for(int i=2;i<n;i++)
		{
			for(int j=head[i];j;j=nxt[j])
			{
				ne=nxt[j],t=to[j];
				nxt[j]=to[j]=0;
				f=0;
				dfs(1);
				nxt[j]=ne,to[j]=t;
				if(!f)
				{
					if(!ans[i])
						res++;
					if(!ans[t]&&t!=n&&t!=1)
						res++;
					ans[i]=ans[t]=1;
				}
				for(int k=1;k<=n;k++)
					flag[k]=false;
			}
		}
		printf("%d\n",res);
		res=0;
		for(int i=2;i<n;i++)
			if(ans[i])
			{
				ans[i]=0;
				printf("%d ",i);
			}
		ans[1]=ans[n]=0;
		printf("\n");
	}
	return 0;
}

赛后代码 30pts(tarjan改天再说,特殊性质懒得打了)
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
int T,n,m,tot,cnt,res,ans[N];
int head[N],nxt[N<<1],to[N<<1];
bool flag[N],f;
inline void add(int u,int v)
{
	nxt[++tot]=head[u];
	head[u]=tot;
	to[tot]=v;
	return ;
}
inline void dfs(int x)
{
	flag[x]=true;
	if(x==n)
		f=1;
	if(f)
		return ;
	int y;
	for(int i=head[x];i;i=nxt[i])
	{
		y=to[i];
		if(!flag[y])
			dfs(y);
	}
	return ;
}
int main()
{
	cin>>T;
	while(T--)
	{
		for(int i=1;i<=n;i++)
			head[i]=0;
		scanf("%d%d",&n,&m);
		for(int i=1,u,v;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		for(int i=2;i<n;i++)
		{
			flag[i]=1;
			f=0;
			dfs(1);
			if(!f)
			{
				res++;
				ans[i]=1;
			}
			for(int j=1;j<=n;j++)
				flag[j]=0;
		}
		printf("%d\n",res);
		res=0;
		for(int i=2;i<n;i++)
			if(ans[i])
			{
				ans[i]=0;
				printf("%d ",i);
			}
		printf("\n");
	}
	return 0;
}

/*
2
4 3
1 2
2 3
3 4
5 5
1 2
2 3
3 4
4 5
4 1
*/

update on 17:22 1/24

40 pts
#include<bits/stdc++.h>
using namespace std;
const int N=400100;
int T,n,m;
int head[11][N],nxt[11][N],to[11][N];
int low[11][N],dfn[11][N],flag[11][N];
int t=1,root,ans,mark[11][N],f[11][N],fa[11][N];
queue<int>q;
void add(int u,int v)
{
	nxt[T][++t]=head[T][u];
	head[T][u]=t;
	to[T][t]=v;
}
void tarjan(int x)
{
	if(x==n)
		mark[T][x]=1;
	low[T][x]=dfn[T][x]=++t;
	int fl=0;
	for(int i=head[T][x];i;i=nxt[T][i])
	{
		int y=to[T][i];
		if(!dfn[T][y])
		{
			tarjan(y);
			low[T][x]=min(low[T][x],low[T][y]);
			if(low[T][y]>=dfn[T][x])
			{
				fl++;
				if(x!=root||fl>1)
					flag[T][x]=true;
			}
		}
		else
			low[T][x]=min(low[T][x],dfn[T][y]);
	}
	return ;
}
inline void bfs(int x)
{
	queue<int>que;
	que.push(x);
	f[T][x]=1;
	while(!que.empty())
	{
		x=que.front();
		que.pop();
		for(int i=head[T][x],y;i;i=nxt[T][i])
		{
			y=to[T][i];
			if(f[T][y])
				continue;
			if(y==n)
			{
				while(x!=1)
				{
					mark[T][x]=1;
					x=fa[T][x];
				}
				return ;
			}
			que.push(y);
			f[T][y]=1;
			fa[T][y]=x;
		}
	}
	return ;
}
int main()
{
//	freopen("in.in","r",stdin);
//	freopen("out.out","w",stdout);
	cin>>T;
	while(T--)
	{
		t=root=ans=0;
		cin>>n>>m;
		for(int i=1,u,v;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			if(u==v)
				continue;
			add(u,v);
			add(v,u);
		}
		t=0;
		for(int i=1;i<=n;i++)
			if(!dfn[T][i])
			{
				root=i;
				tarjan(i);
			}
		bfs(1);
		for(int i=2;i<n;i++)
			if(flag[T][i]&&mark[T][i])
			{
				ans++;
				q.push(i);
			}
		cout<<ans<<'\n';
		while(!q.empty())
		{
			printf("%d ",q.front());
			q.pop();
		}
		printf("\n");
	}
	return 0;
}

update on 6.26

100pts
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,n,m,ans,f;
int head[N],nxt[N],to[N],tot;
int dfn[N],low[N],cnt,flag[N];
stack<int>s;
vector<int>v[N];
priority_queue<int>q;
inline void init()
{
//	memset(head,0,sizeof(head));
//	memset(nxt,0,sizeof(nxt));
//	memset(to,0,sizeof(to));
//	memset(dfn,0,sizeof(dfn));
//	memset(low,0,sizeof(low));
//	memset(flag,0,sizeof(flag));
	for(int i=1;i<=n*2;i++)
		head[i]=dfn[i]=low[i]=flag[i]=0;
	for(int i=1;i<=tot;i++)
		nxt[i]=to[i]=0;
	for(int i=1;i<=cnt;i++)
		v[i].clear();
	ans=tot=f=0;
	return ;
}
inline void add(int u,int v)
{
	nxt[++tot]=head[u];
	head[u]=tot;
	to[tot]=v;
	return ;
}
inline void tarjan(int x)
{
	dfn[x]=low[x]=++tot;
	s.push(x);
	for(int i=head[x],y,z;i;i=nxt[i])
	{
		y=to[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if(low[y]==dfn[x])
			{
				++cnt;
				do
				{
					z=s.top();
					s.pop();
					v[cnt].push_back(z);
					v[z].push_back(cnt);
				}while(z!=y);
				v[x].push_back(cnt);
				v[cnt].push_back(x);
			}
		}
		else
			low[x]=min(low[x],dfn[y]);
	}
	return ;
}
inline void dfs(int x)
{
	if(x==n)
		return ;
	for(auto it : v[x])
		if(!flag[it])
		{
			flag[it]=x;
			dfs(it);
		}
	return ;
}
int main()
{
	freopen("home.in","r",stdin);
	freopen("home.out","w",stdout);
	cin>>T;
//	T=1;
	while(T--)
	{
		init();
		scanf("%d%d",&n,&m);
		cnt=n;
		for(int i=1,u,v;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		tot=0;
		for(int i=1;i<=n;i++)
			if(!dfn[i])
			{
				tarjan(i);
				s.pop();
			}
		dfs(1);
		int x=flag[n];
		while(x!=1)
		{
			if(x<n)
			{
				ans++;
				q.push(-x);
			}
			x=flag[x];
		}
		printf("%d\n",ans);
		while(!q.empty())
		{
			printf("%d ",-q.top());
			q.pop();
		}
		printf("\n");
	}
	return 0;
}

T3

题意:两种颜色的寿司围成一个圈,每次移动相邻的两个,问最少多少次能让他们分开(形成两个连续的区域)

刚开始只有乱搞的思路,题目不知道为啥还疯狂暗示你这道题用数据结构(平衡树)

后来想到可以转换成链然后 前缀和 + 差分
想来会有很多问题,但没啥时间所以没想更优的做法

赛事+赛后代码 15pts(不会)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,len,tb,ans;
int b[N],q[N];
char c[N];
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%s",c);
		len=strlen(c);
		for(int i=len;i>=1;i--)
			c[i]=c[i-1];
		c[0]=0;
		for(int i=1;i<=len;i++)
			if(c[i]=='R')
				b[++tb]=i;
		for(int i=tb+1;i<=tb*2;i++)
			b[i]=b[i-tb]+len;
		for(int i=1;i<=tb*2;i++)
			q[i]=q[i-1]+b[i]-b[i-1]-1;
		ans=N<<5;
		for(int i=1;i<=tb*2;i++)
		{
			int res=0;
			for(int j=1;j<=tb;j++)
			{
				if(j==i||j+tb==i)
					continue;
				res+=min(abs(q[j]-q[i]),abs(q[j+tb]-q[i]));
			}
			ans=min(ans,res);
		}
		printf("%d\n",ans);
	}
	return 0;
}

/*
1
BBRBBRBBBRRR
*/

想象中的:
image

实际上的:
image

对了,走的时候被 \(Huge\) D了,让我期末考好一点,就当给期末攒 \(RP\) 了吧

顺便放上本次放假最大收获:
(可惜不知道为啥游戏里新皮肤两只眼睛跟长分家了一样,过活动的时候老难受了)

posted @ 2024-01-22 12:18  lxyt-415x  阅读(86)  评论(3编辑  收藏  举报