模拟58 考试总结

不要停下来啊

考试经过

一言难尽。。。
把T1无向图看成有向图,然后直接弃了,T250分没有,原因是没注意不是质数没有逆元
T3没看出来AC自动机写的暴力,T4倒是胡了个网络流暴力,然而数组开小少了10分
现在犯过的错误以后一定不能再犯了,尤其是这个质数

T2.贝尔数

别问为啥没T1
模数非质数,所以暴力的话不能用费马也不能线性,只能杨辉三角递推
正解是用CRT把大数拆成5个质数连乘,每个用矩阵快速幂算就行

#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int modd=95041567;
int m[7]={0,31,37,41,43,47};
int f[6][55][55],bell[6][55];
inline int ksm(int x,int y,int mod)
{
	int s=1;x%=mod;
	for(;y;y>>=1)
	{
		if(y&1)s=s*x%mod;
		x=x*x%mod;
	}
	return s;
}
inline int ny(int x,int mod){return ksm(x,mod-2,mod);}
int a[50],b[50],p[50][50],pp[50][50];
inline void getp(int mod)
{
	memset(p,0,sizeof(p));
	for(int i=2;i<=mod;i++)p[i][i-1]=1;
	p[1][mod]=p[2][mod]=1;
}
inline void mul(int mod)
{
	memset(pp,0,sizeof(pp));
	for(int i=1;i<=mod;i++)
	 for(int j=1;j<=mod;j++)
	  for(int k=1;k<=mod;k++)
	   pp[i][j]=(pp[i][j]+p[i][k]*p[k][j]%mod)%mod;
	memcpy(p,pp,sizeof(p));
}
inline void gan(int mod)
{
	memset(b,0,sizeof(b));
	for(int i=1;i<=mod;i++)
	 for(int j=1;j<=mod;j++)
	  b[i]=(b[i]+a[j]*p[j][i]%mod)%mod;
	memcpy(a,b,sizeof(b));
}
inline void ga(int x,int mod)
{
	for(;x;x>>=1)
	{
		if(x&1)gan(mod);
		mul(mod);
	}
}
signed main()
{
	freopen("bell.in","r",stdin);
	freopen("bell.out","w",stdout);
	for(int x=1;x<=5;x++)
	{
		int mod=m[x];
		f[x][0][0]=1;
		for(int i=1;i<=50;i++)
		{
			f[x][i][0]=1;
			for(int j=1;j<i;j++)f[x][i][j]=(f[x][i-1][j-1]+f[x][i-1][j])%mod;
			f[x][i][i]=1;
		}
		bell[x][0]=1;
		for(int i=1;i<=mod;i++)
		 for(int j=0;j<i;j++)
		  bell[x][i]=(bell[x][i]+f[x][i-1][j]*bell[x][j]%mod)%mod;
	}
	int t;cin>>t;
	while(t--)
	{
		int n;scanf("%lld",&n);
		int ans=0;
		for(int x=1;x<=5;x++)
		{
			int mod=m[x],mm=modd/mod;
			getp(mod);memset(a,0,sizeof(a));
			for(int i=1;i<=mod;i++)a[i]=bell[x][i];
			ga(n-1,mod);int an=a[1];
			ans=(ans+an*mm%modd*ny(mm,mod)%modd)%modd;		
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T3. 穿越广场

AC自动机dp,考场没看出来
设状态\(f_{i,j,k,s}\)表示当前串长,其中一个字母数量,节点,匹配状态(二进制),随便转移就行
注意D和R要一一对应,包括节点和状态

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=205;
const int mod=1e9+7;
int tr[N][2],fail[2*N],tot=1;
char s[3][N];// D 0 R 1
int v[N*2]; 
inline void ins(int x)
{
	int u=1,l=strlen(s[x]+1);
	for(int i=1;i<=l;i++)
	{
		int op=(s[x][i]=='D')?0:1;
		if(!tr[u][op])tr[u][op]=++tot;
		u=tr[u][op];
	}	
	v[u]|=(1<<(x-1));
}
queue <int> q;
inline void build()
{
	for(int i=0;i<=1;i++)
	{
		if(tr[1][i])q.push(tr[1][i]),fail[tr[1][i]]=1;
		else tr[1][i]=1;
	}
	while(q.size())
	{
		int u=q.front();q.pop();
		if(v[fail[u]])v[u]|=v[fail[u]];
		for(int i=0;i<=1;i++)
		{
			if(tr[u][i])q.push(tr[u][i]),fail[tr[u][i]]=tr[fail[u]][i];
			else tr[u][i]=tr[fail[u]][i];
		}
	}
}	
int f[N][105][N][4];
inline void clear()
{		
	memset(tr,0,sizeof(tr));tot=1;
	memset(fail,0,sizeof(fail));
	memset(v,0,sizeof(v));
	memset(f,0,sizeof(f));
}
signed main()
{	
	freopen("square.in","r",stdin);
	freopen("square.out","w",stdout);
	int t;cin>>t;
	while(t--)
	{
		clear();
		int m,n;scanf("%lld%lld",&m,&n);
		scanf("%s%s",s[1]+1,s[2]+1);
		ins(1);ins(2);build();
		f[0][0][1][0]=1;
		for(int i=0;i<=n+m;i++)
		 for(int j=0;j<=n;j++)
		  for(int k=1;k<=tot;k++)
		   for(int s=0;s<=3;s++)
		   {
		   	f[i+1][j+1][tr[k][0]][s|v[tr[k][0]]]=(f[i+1][j+1][tr[k][0]][s|v[tr[k][0]]]+f[i][j][k][s])%mod;
		   	f[i+1][j][tr[k][1]][s|v[tr[k][1]]]=(f[i+1][j][tr[k][1]][s|v[tr[k][1]]]+f[i][j][k][s])%mod;
		   }		 	
		int ans=0;
		for(int i=1;i<=tot;i++)ans=(ans+f[n+m][n][i][3])%mod;
		printf("%lld\n",ans);
	}
	return 0;
}

T4.舞动的夜晚

蓝书上原题,二分图匹配可行边
证明看蓝书吧,结论也有,全是板子冲就完了

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=20050,M=200050;
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
struct node1{
	int from,to,next,w;
}a[2*M];
int head[N],mm=2;
inline void add(int x,int y,int w)
{
	a[mm].from=x;a[mm].to=y;a[mm].w=w;
	a[mm].next=head[x];head[x]=mm++;
}
inline void gan(int x,int y,int w)
{
	add(x,y,w);add(y,x,0);
}
int s,t,n,m,p,tot;
int d[N],hd[N];
queue <int> q;
inline bool bfs()
{	
	memset(d,0x3f,sizeof(d));
	memcpy(head,hd,sizeof(head));
	while(q.size())q.pop();
	q.push(s);d[s]=0;
	while(q.size())
	{
		int x=q.front();q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(!a[i].w)continue;
			if(d[y]>d[x]+1)d[y]=d[x]+1,q.push(y);
		}
		if(x==t)return 1;
	}
	return 0;
}
int dfs(int x,int flow)
{
	if(x==t)return flow;
	int rest=flow,k;
	for(int i=head[x];i;head[x]=i=a[i].next)
	if(a[i].w)
	{
		int y=a[i].to;
		if(d[y]==d[x]+1)
		{
			k=dfs(y,min(a[i].w,rest));
			if(!k)d[y]=0;
			else a[i].w-=k,a[i^1].w+=k,rest-=k;
		}
		if(!rest)break;
	}
	return flow-rest;
}
int mp1[N],mp2[N],r[M];
struct node2{
	int from,to,next;
}b[2*M];
inline void add2(int x,int y)
{
	b[mm].from=x;b[mm].to=y;
	b[mm].next=head[x];head[x]=mm++;
}
int dfn[N],low[N],ga[N],num,idd;
vector <int> an;
stack <int> st;bool v[N];
void tarjan(int x)
{
	dfn[x]=low[x]=++num;
	st.push(x);v[x]=1;
	for(int i=head[x];i;i=b[i].next)
	{
		int y=b[i].to;
		if(!dfn[y])
		{	
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(v[y])low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		int top;idd++;
		do{
			top=st.top();v[top]=0;
			ga[top]=idd;st.pop();	
		}while(x!=top);
	}
}
signed main()
{
	freopen("night.in","r",stdin);
	freopen("night.out","w",stdout);
	cin>>n>>m>>p;
	s=++tot;t=++tot;
	for(int i=1;i<=n;i++)mp1[i]=++tot,gan(s,mp1[i],1);
	for(int i=1;i<=m;i++)mp2[i]=++tot,gan(mp2[i],t,1);
	for(int i=1;i<=p;i++)
	{
		int x=read(),y=read();
		r[i]=mm;
		gan(mp1[x],mp2[y],1);
	}
	memcpy(hd,head,sizeof(head));
	while(bfs())dfs(s,1e9);
	mm=1;int sum=0;memset(head,0,sizeof(head));
	for(int i=1;i<=p;i++)
	{
		int id=r[i],x=a[id].from,y=a[id].to;
		if(a[id].w)add2(x,y);
		else add2(y,x);
	}
	for(int i=hd[s];i;i=a[i].next)
	{
		int y=a[i].to;
		if(a[i].w)add2(s,y);
		else add2(y,s);
	}
	for(int i=hd[t];i;i=a[i].next)
	{
		int y=a[i].to;
		if(a[i^1].w)add2(y,t);
		else add2(t,y);
	}
	for(int i=1;i<=tot;i++)if(!dfn[i])tarjan(i);	
	for(int i=1;i<=p;i++)
	{		
		int id=r[i],x=a[id].from,y=a[id].to;
		if(!a[id].w)continue;
		if(ga[x]==ga[y])continue;
		sum++;an.push_back(i);
	}
	cout<<sum<<endl;
	for(int i=0;i<an.size();i++)printf("%lld ",an[i]);
	return 0;
}

T1.Lesson5!

因为这个本来改A了,结果luogu上被hack了,调了一上午。。。
有向图显然可以拓扑排序,正反做一遍,那么一条路径可以表示为他起点正拓扑序+终点逆拓扑序+1
由于每一条边都可以表示一条路径,所以可以方便解决删点问题
按正拓扑序枚举点,实现一个数据结构存最长路,扫到这个点时:
删掉入边——统计答案——加入出边
一个多重集完全可以实现,可删堆或者权值线段树也是可以的
强烈建议建出源汇点,这样省很多细节
原理的话是拓扑序保证了不重不漏,所有边也都统计过,反正大佬的题解都挺清楚的。。。
放一个某谷上AC代码,没加文件和多测

#include <bits/stdc++.h>
using namespace std;
const int N=500050,M=1000050;
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
struct node{
	int from,to,next,op;
}a[2*M];
int head[N],mm=1;
inline void add(int x,int y,int op)
{
	a[mm].from=x;a[mm].to=y;a[mm].op=op;
	a[mm].next=head[x];head[x]=mm++;
}
int n,m,du1[N],du2[N];
int p1[N],p2[N];
vector <int> num[N];
multiset <int> s;
queue <int> q;
inline void de(int x)
{
	auto it=s.find(x);
	if(it!=s.end())s.erase(it);
}
inline int get()
{
	if(s.size())return *s.rbegin();
	else return (int)1e9;
}
signed main()
{
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		add(x,y,1);add(y,x,0);
		du1[y]++;du2[x]++;
	}
	int ss=n+1,tt=n+2;
	for(int i=1;i<=n;i++)
	{	
		add(ss,i,1);add(i,ss,0);
		add(i,tt,1);add(tt,i,0);
		du1[i]++;du2[ss]++;
		du1[tt]++;du2[i]++;	
	}
	q.push(ss);
	while(q.size())
	{
		int x=q.front();q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			if(a[i].op==0)continue;
			int y=a[i].to;
			if(p1[y]<p1[x]+1)p1[y]=p1[x]+1;
			du1[y]--;if(!du1[y])q.push(y);
		}
	}
	q.push(tt);
	while(q.size())
	{
		int x=q.front();q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			if(a[i].op==1)continue;
			int y=a[i].to;
			if(p2[y]<p2[x]+1)p2[y]=p2[x]+1;
			du2[y]--;if(!du2[y])q.push(y);	
		}
	}
	for(int i=1;i<=n+2;i++)num[p1[i]].push_back(i);
	int ans=1e9,anss=1e9;
	for(int i=0;i<=n+2;i++)
	{
		if(!num[i].size())continue;
		for(int j=0;j<num[i].size();j++)
		{
			int x=num[i][j];
			for(int ii=head[x];ii;ii=a[ii].next)
			{
				int y=a[ii].to;
				if(a[ii].op==1)continue;
				int w=p1[y]+p2[x]-1;
				de(w);
			}
			int an=get();
		   if(an<ans)ans=an,anss=x;
		   else if(an==ans)anss=min(anss,x);
			for(int ii=head[x];ii;ii=a[ii].next)
			{	
				int y=a[ii].to;
				if(a[ii].op==0)continue;
				int w=p1[x]+p2[y]-1;
				s.insert(w);
			}
		}
	}
	printf("%d %d\n",anss,ans);
	return 0;
}

考试总结

细节还是细节,任何一个细小的地方都会引起大的失误或挂分
多打表,多对拍,多动键盘,不要干坐着

posted @ 2021-09-24 14:14  D'A'T  阅读(48)  评论(0)    收藏  举报