2023.8.11测试

暑假NOIP模拟赛-4

为什么我天天挂分?????????

T1 数对

其实就是[ABC206E] Divide Both

简单容斥+莫反,但是最后10min才发现容斥写错了!!!!!(幸好贺了别人的代码通过了

calc(l,r) 表示 x[1,l]y[1,r] 的答案,那么 ans=calc(r,r)calc(l1,r)calc(r,l1)+calc(l1,l1)

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N=1000010;

int L,R;
int v[N],prime[N],tot,mu[N];
LL ans,sum[N];

void primes()
{
	mu[1]=1;
	for(int i=2; i<=1e6; i++)
	{
		if(!v[i])
		{
			v[i]=i;
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(int j=1; j<=tot && prime[j]<=v[i] && prime[j]<=1e6/i; j++)
		{
			v[i*prime[j]]=prime[j];
			if(i%prime[j]==0)
				break;
			mu[i*prime[j]]=-mu[i];
		}
	}
	
	for(int i=1; i<=1e6; i++)
		sum[i]=sum[i-1]+(LL)mu[i];
}

LL calc(int n,int m)
{
	LL res=1LL*n*m;
	for(int l=1,r; l<=n; l=r+1)
	{
		r=min(n/(n/l),m/(m/l));
		res-=1LL*(n/l)*(m/l)*(sum[r]-sum[l-1]);
	}
	for(int i=2; i<=n; i++)
		res-=1LL*((n/i)+(m/i)-1);
	return res;
}

int main()
{
	primes();
	
	scanf("%d%d",&L,&R);
	ans=calc(R,R)-2*calc(L-1,R)+calc(L-1,L-1);
	
	printf("%lld\n",ans);
	
	return 0;
}

T2 小偷与警察

其实就是P4334 [COI2007] Policija

很好的点双练习题,逼我赶紧去恶补圆方树,需要赶紧修订图的连通性相关(Tarjan算法)

先跑一遍 Tarjan,求出所有割点和割边,这步一眼。对于不是割边的边或割点的点,询问时显然直接输出yes

对于割边和割点,边双缩点和点双缩点都要做一遍???

不用!我们惊奇地发现一条割边的两个端点一定是割点,所以可以直接将边的询问转化为点的询问

那现在问题变成了如何确定一个点是否可以拦截罪犯。

对原图进行点双缩点,建出圆方树,那么对于 (a,b,x) 的询问,只需查询 x 是否在 a,b 两点的简单路径上即可,树剖可做。对于一条割边,它在圆方树上对应着一个方点,拿个 map 存一下编号即可

#include<bits/stdc++.h>
#define mp make_pair
using namespace std;

const int N=100010,M=500010,NN=200010;

int n,m,q;
int head[N],ver[2*M],nxt[2*M],tot;
int dfn[N],low[N],num,cut[N],c[N],cnt,sta[N],topp,flag;
int fa[NN],siz[NN],dep[NN],son[NN],top[NN],dft[NN];
vector <int> g[NN];
map < pair<int,int>,int > bridge;

pair<int,int> minmax(int x,int y)
{
	return mp(min(x,y),max(x,y));
}

void add(int x,int y)
{
	ver[++tot]=y;  nxt[tot]=head[x];  head[x]=tot;
	ver[++tot]=x;  nxt[tot]=head[y];  head[y]=tot;
}

void add_c(int x,int y)
{
	g[x].push_back(y);
	g[y].push_back(x);
}

void tarjan(int x,int fa)
{
	dfn[x]=low[x]=++num;
	sta[++topp]=x;
	for(int i=head[x]; i; i=nxt[i])
	{	
		int y=ver[i];
		if(!dfn[y])
		{
			tarjan(y,x);
			low[x]=min(low[x],low[y]);
			
			if(dfn[x]<=low[y])
			{
				flag++;
				if(flag>1 || x!=1)
					cut[x]=1;
				cnt++;  add_c(cnt,x);
				if(dfn[x]<low[y])
					bridge[minmax(x,y)]=cnt;
				int z;
				do
				{
					z=sta[topp--];
					add_c(cnt,z);
				}while(y!=z);
			}
		}
		else if(y!=fa)
			low[x]=min(low[x],dfn[y]);
	}
}

void dfs1(int x,int f)
{
	son[x]=-1;  siz[x]=1;
	dep[x]=dep[fa[x]=f]+1;
	for(int i=0; i<g[x].size(); i++)
	{
		int y=g[x][i];
		if(y==f)
			continue;
		dfs1(y,x);
		if(son[x]==-1 || siz[y]>siz[son[x]])
			son[x]=y;
		siz[x]+=siz[y];
	}
}

void dfs2(int x,int t)
{
	top[x]=t;  dft[x]=++dft[0];
	if(son[x]==-1)
		return;
	dfs2(son[x],t);
	for(int i=0; i<g[x].size(); i++)
	{
		int y=g[x][i];
		if(y==fa[x] || y==son[x])
			continue;
		dfs2(y,y);
	}
}

bool check(int x,int y,int z)
{
	bool pd=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])
			swap(x,y);
		if(top[x]==top[z] && dep[x]>=dep[z])
			pd=1;
		x=fa[top[x]];
	}
	if(pd)
		return 1;
	if(dep[x]<dep[y])
		swap(x,y);
	if(top[x]==top[z] && dep[x]>=dep[z] && dep[y]<=dep[z])
		return 1;
	return 0;
} 

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	
	cnt=n;
	tarjan(1,0); 
	dfs1(1,0);
	dfs2(1,1);
	
	scanf("%d",&q);
	while(q--)
	{
		int op,a,b,c,x,y;
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d%d%d",&a,&b,&x,&y);
			map< pair<int,int>,int>::iterator it=bridge.find(minmax(x,y));
			if(it==bridge.end())
				printf("yes\n");
			else if(check(a,b,it->second))
				printf("no\n");
			else
				printf("yes\n");
		}
		else
		{
			scanf("%d%d%d",&a,&b,&x);
			if(!cut[x])
				printf("yes\n");
			else if(check(a,b,x))
				printf("no\n");
			else
				printf("yes\n");	
		}
	}

	return 0;
}

T3 无线网络

其实就是P5786 [CQOI2008] 传感器网络

无限接近正解,但还是输了……

二分答案最后的负担值 mid,跑一次网络流验证是否满流即可。考场上想到这里,不会处理字典序,再加上数组开小、二分边界设错等原因,喜提 20

其实字典序处理很简单,在我们二分知道最后的负担值 ans 后,直接从小到大暴力枚举 i 的父亲 fa[i],再跑网络流验证即可

#include<bits/stdc++.h>
using namespace std;

const int N=60,M=3010,INF=1e9;

int n,m,s,t,maxflow,id,d[2*N],fa[N];
int head[2*N],ver[2*M],nxt[2*M],edge[2*M],now[2*N],tot=1;
char str[N][N];

void add(int x,int y,int z)
{
	ver[++tot]=y;  edge[tot]=z;  nxt[tot]=head[x];  head[x]=tot;
	ver[++tot]=x;  edge[tot]=0;  nxt[tot]=head[y];  head[y]=tot;
}

bool bfs()
{
	memset(d,0,sizeof(d));
	queue <int> q;
	q.push(s);  d[s]=1;
	now[s]=head[s];
	
	while(q.size())
	{
		int x=q.front();  q.pop();
		for(int i=head[x]; i; i=nxt[i])
		{
			if(edge[i] && !d[ver[i]])
			{
				int y=ver[i];
				q.push(y);
				now[y]=head[y];
				d[y]=d[x]+1;
				if(y==t)  
					return 1;
			}
		}
	}
	
	return 0;
}

int dinic(int x,int flow)
{
	if(x==t)
		return flow;
	int rest=flow,k;
	
	for(int &i=now[x]; i && rest; i=nxt[i])
	{
		if(edge[i] && d[ver[i]]==d[x]+1)
		{
			int y=ver[i];
			k=dinic(y,min(rest,edge[i]));
			if(!k)
				d[y]=0;
			edge[i]-=k;
			edge[i^1]+=k;
			rest-=k;
			if(rest<=0)
			    break;
		}
	}
	
	return flow-rest;
}

void Dinic()
{
	int flow=0;
	while(bfs())
		while(flow=(dinic(s,INF)))
			maxflow+=flow;
	return;
}

bool check(int k)
{
	tot=1;
	memset(head,0,sizeof(head));
	for(int i=1; i<=n; i++)
	{
		add(s,i,1);
		if(fa[i])
			add(i,fa[i]+n,1);
		else if(str[i][n+1]=='Y')
			add(i,t,1);
		else
			for(int j=1; j<=n; j++)
				if(str[i][j]=='Y')
					add(i,j+n,1);
		add(i+n,t,k);
	}
	
	maxflow=0;
	Dinic();
	if(maxflow==n)
		return 1;
	return 0;
}

int find()
{
	int lo=-1,hi=n;
	while(lo+1<hi)
	{
		int mid=(lo+hi)>>1;
		if(check(mid))
			hi=mid;
		else
			lo=mid;
	}
	return hi;
}

int main()
{
	scanf("%d",&n);
	for(int i=0; i<=n; i++)
		scanf("%s",str[i]+1);
	
	for(int i=1; i<=n; i++)
		str[i][n+1]=str[0][i];
	
	s=0;  t=2*n+1;
	
	int ans=find();
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n+1; j++)
		{
			if(str[i][j]=='Y')
			{
				fa[i]=j;
				if(check(ans))
					break;
			}
		}
	}
		
	for(int i=1; i<=n; i++)
		printf("%d ",fa[i]-1);

	return 0;
}

T4 游戏设计

对于所有长度为 k 的字符串,每个字符是 A,B,C,D 4 个中的一个,显然共有 4k 个字符串,现在要给每个字符串分配一种颜色。同时,给定 m 个长度小于 k 的字符串 Bi,Ci

对于一个字符串 S,可以对它进行以下 2 种操作之一:

  • 交换相邻的两个字母

  • 如果字符串中含有子串 B,可以将它替换成子串 C(保证 |B|=|C|

如果能从某个字符串 S 出发,经过操作变成字符串 T,且 ST 的颜色相同,即为胜利。问要使得永远无法胜利所需要的最少颜色数量是多少?

多组测试数据,1T101k301m50

赛时没开,其实不是很难

因为可以交换相邻的字符,所以每种字母出现个数相同的字符串可以通过操作 1 相互到达。因此字符串本身是什么不重要,重要的是每种字母的个数。于是我们用一个四元组 (a,b,c,d) 来表示一个状态,共有 (k+4141)(30+4141)=5456 个这样的状态。对于每种状态,设它的权值为 val=k!a!b!c!d!=(a+b+c+da)(b+c+db)(c+dd)(多重排列方案数)

容易发现,有些状态可以通过操作 2 相互到达,且可能存在环,于是建完图后一发 SCC 缩点,对于每个强联通分量,定义它的权值 a 为内部各个点的 val 和。之后拓扑排序找最长路即可

#include<bits/stdc++.h>
#define LL long long
#define mp make_pair
using namespace std;

const int N=6010,NN=40,M=60,MM=36000010;

int T,k,m,n,cnt[55][4][2],deg[N],tid;
int dfn[N],low[N],ins[N],c[N],sta[N],top,tc;
int head[N],ver[MM],nxt[MM],from[MM],tot;
LL C[NN][NN],val[N],a[N],f[N],ans;
char str1[M][NN],str2[M][NN];
vector <int> scc[N],g[N];
map <pair< pair<int,int>,pair<int,int> >,int> fid;

void init()
{
	tot=top=ans=tid=0;
	memset(head,0,sizeof(head));
	memset(f,0,sizeof(f));
	memset(cnt,0,sizeof(cnt));
	memset(deg,0,sizeof(deg));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(c,0,sizeof(c));
	fid.clear();
	for(int i=1; i<=tc; i++)
		scc[i].clear(),g[i].clear();
	tc=0;
}

void prework()
{
	for(int i=0; i<=35; i++)
		C[i][0]=1;
	for(int i=1; i<=35; i++)
		for(int j=1; j<=i; j++)
			C[i][j]=C[i-1][j-1]+C[i-1][j];
}

void add(int x,int y)
{
	ver[++tot]=y;  from[tot]=x;
	nxt[tot]=head[x];  head[x]=tot;
}

void add_c(int x,int y)
{
	g[x].push_back(y);  deg[y]++;
}

void tarjan(int x)
{
	dfn[x]=low[x]=++dfn[0];
	sta[++top]=x;  ins[x]=1;
	for(int i=head[x]; i; i=nxt[i])
	{
		int y=ver[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y])
			low[x]=min(low[x],dfn[y]);
	}
	
	if(dfn[x]==low[x])
	{
		tc++;  int y;
		do
		{
			y=sta[top--];  ins[y]=0;
			c[y]=tc;  scc[tc].push_back(y);
		}while(x!=y);
	}
}

LL count(int a,int b,int c,int d)
{
	return C[a+b+c+d][a]*C[b+c+d][b]*C[c+d][c];
}

void topsort()
{
	queue <int> q;
	for(int i=1; i<=tc; i++)
		if(!deg[i])	
			f[i]=a[i],q.push(i);
	while(q.size())
	{
		int x=q.front();  q.pop();
		int len=g[x].size();
		for(int i=0; i<len; i++)
		{
			int y=g[x][i];
			f[y]=max(f[y],f[x]+a[y]);
			deg[y]--;
			if(!deg[y])
				q.push(y);
		}
	}
	
	for(int i=1; i<=tc; i++)
		ans=max(ans,f[i]);
}

int main()
{
	prework();
	
	scanf("%d",&T);
	while(T--)
	{
		init();
		
		scanf("%d%d",&k,&m);
		
		for(int a=0; a<=k; a++)
		{
			for(int b=0; a+b<=k; b++)
			{
				for(int c=0; a+b+c<=k; c++)
				{
					int d=k-a-b-c;
					fid[mp(mp(a,b),mp(c,d))]=++tid;
					val[tid]=count(a,b,c,d);
				}
			}
		}
		
		for(int i=1; i<=m; i++)
		{
			scanf("%s",str1[i]+1);
			int len=strlen(str1[i]+1);
			for(int j=1; j<=len; j++)
				cnt[i][str1[i][j]-'A'][0]++;
		}
		for(int i=1; i<=m; i++)
		{
			scanf("%s",str2[i]+1);
			int len=strlen(str2[i]+1);
			
			for(int j=1; j<=len; j++)
				cnt[i][str2[i][j]-'A'][1]++;
			
			int aa=max(cnt[i][0][0],cnt[i][0][0]-cnt[i][0][1]);
			int bb=max(cnt[i][1][0],cnt[i][1][0]-cnt[i][1][1]);
			int cc=max(cnt[i][2][0],cnt[i][2][0]-cnt[i][2][1]);
			int dd=max(cnt[i][3][0],cnt[i][3][0]-cnt[i][3][1]);
			for(int a=aa; a+bb+cc+dd<=k; a++)
			{
				for(int b=bb; a+b+cc+dd<=k; b++)
				{
					for(int c=cc; a+b+c+dd<=k; c++)
					{
						int d=k-a-b-c;
						int id=fid[mp(mp(a,b),mp(c,d))];
						int aaa=a-(cnt[i][0][0]-cnt[i][0][1]);
						int bbb=b-(cnt[i][1][0]-cnt[i][1][1]);
						int ccc=c-(cnt[i][2][0]-cnt[i][2][1]);
						int ddd=d-(cnt[i][3][0]-cnt[i][3][1]);
						int idd=fid[mp(mp(aaa,bbb),mp(ccc,ddd))]; 
						if(id==idd)
							continue;
						add(id,idd);
					}
				}
			}
		}
		
		n=C[k+4-1][4-1];
		for(int i=1; i<=n; i++)
			if(!dfn[i])
				tarjan(i);	
		for(int i=1; i<=tot; i++)
		{
			int x=from[i],y=ver[i];
			if(c[x]==c[y])
				continue;
			add_c(c[x],c[y]);
		}
		for(int i=1; i<=tc; i++)
		{
			int len=scc[i].size();  a[i]=0;
			for(int j=0; j<len; j++)
				a[i]+=val[scc[i][j]];
		}
		
		topsort();
		
		printf("%lld\n",ans);
	}

	return 0;
}

100+18+20+0=138,这都有 rk13???

总结

  • T1 没测大样例就直接跑路了,以后还是需要谨慎些,对于这种数学题要多测几组大样例

  • T2 写错解写了太久了,以后写代码前应试试自己做法是否正确,拿样例试一下,不能直接开写。对于圆方树的知识点,不会没办法

  • T3 对于字典序的处理有失偏颇,应该要注意到数据范围很小可以暴力枚举的。数组开小这种问题不能再犯

posted @   xishanmeigao  阅读(23)  评论(0编辑  收藏  举报
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示