2021.9.21考试总结[NOIP模拟58]

T1 lesson5!

开始以为是个无向图,直接不懂,跳去T2了。

之后有看了一眼发现可暴力,于是有了\(80pts\)

发现这个图是有拓扑序的,于是可以用拓扑排序找最长路径。先找原图内在最长路径上的点,挨个删了跑拓扑排,看哪个最短。

正解太nb了待补。

\(code:\)

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

namespace IO{
	inline int read(){
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	inline void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0){ putchar('-'); x=~x+1; }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	}
	inline int max(int x,int y){ return x<y?y:x; }
	inline int min(int x,int y){ return x<y?x:y; }
	inline void swap(int& x,int& y){ x^=y^=x^=y; }
	inline void ckmax(int& x,int y){ x=x<y?y:x; }
	inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

const int NN=1e5+5,MM=5e5+5;
int t,n,m,mx,ans,pos,ban,idx,to[MM],nex[MM],head[NN],dis[NN],pre[NN];
int l,r,q[NN],in[NN],deg[NN];
bool vis[NN];
vector<int>vec;
inline void add(int a,int b){
	to[++idx]=b; nex[idx]=head[a]; head[a]=idx; ++in[b];
}

void topo(){
	l=1; r=0;
	for(int i=1;i<=n;i++)
		if(!in[i]&&ban!=i) pre[i]=0, dis[i]=0, q[++r]=i;
	while(l<=r){
		int x=q[l++];
		if(dis[x]>mx) mx=dis[x];
		for(int i=head[x];i;i=nex[i]) if(in[to[i]]&&ban!=to[i]){
			--in[to[i]];
			if(!in[to[i]]){
				pre[to[i]]=x;
				dis[to[i]]=dis[x]+1;
				q[++r]=to[i];
			}
		}
	}
}

signed main(){
	FILE *R=freopen("johnny.in","r",stdin);
 	FILE *W=freopen("johnny.out","w",stdout);
	t=read();
	while(t--){
		n=read(); m=read();
		mx=ban=idx=0; vec.clear();
		for(int i=1;i<=n;i++) in[i]=head[i]=vis[i]=0;
		for(int a,b,i=1;i<=m;i++)
			a=read(),b=read(), add(a,b);
		for(int i=1;i<=n;i++) deg[i]=in[i];
		topo(); ans=mx; pos=INT_MAX;
		for(int i=1;i<=n;i++) if(dis[i]==mx){
			int x=i;
			while(x) vec.push_back(x), x=pre[x];
		}
		for(int i=0;i<vec.size();i++) if(!vis[vec[i]]){
			ban=vec[i]; mx=0; vis[ban]=1;
			for(int j=1;j<=n;j++) in[j]=deg[j];
			for(int j=head[ban];j;j=nex[j]) --in[to[j]];
			topo();
			if(mx<ans||(mx==ans&&ban<pos)) ans=mx, pos=ban;
		}
		write(pos,' '); write(ans,'\n');
	}
}

T2 贝尔数

模数不是质数就很搞。。

发现分解质因数后模数是五个一次的两位质数相乘,题目还给了模质数意义下的同余公式,于是可以先求出模五个质数意义下这一位贝尔数的值,然后再用中国剩余定理解个同余方程合并即可。

对每个质数可以先预处理前四十多个贝尔数,然后矩阵加速递推。

个人认为挺神的题(主要是数学跟矩阵都太菜了

\(code:\)

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

namespace IO{
	inline int read(){
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	inline void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0){ putchar('-'); x=~x+1; }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	}
	inline int max(int x,int y){ return x<y?y:x; }
	inline int min(int x,int y){ return x<y?x:y; }
	inline void swap(int& x,int& y){ x^=y^=x^=y; }
	inline void ckmax(int& x,int y){ x=x<y?y:x; }
	inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

const int NN=1010,mod=95041567;
int T,n,nul,c[50][50],bell[50],pre[6],calc[6];
int pri[6]={0,31,37,41,43,47};

namespace Crt{
	int exgcd(int a,int b,int& x,int& y){
		if(!b){
			x=1; y=0;
			return a;
		}
		int g=exgcd(b,a%b,x,y),z;
		z=y; y=x-a/b*y; x=z;
		return g;
	}
	void init(){
		bell[0]=1;
		for(int i=0;i<50;i++) c[i][0]=1;
		for(int i=1;i<50;i++) for(int j=1;j<=i;j++)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
		for(int i=1;i<50;i++) for(int j=0;j< i;j++)
			(bell[i]+=c[i-1][j]*bell[j]%mod)%=mod;
		for(int i=1;i<6;i++){
			int g=exgcd(pri[i],mod/pri[i],nul,pre[i]);
			pre[i]=(pre[i]%mod+mod)%mod; pre[i]=(mod/pri[i])*pre[i]%mod;
		}
	}
	int solve(){
		int res=0;
		for(int i=1;i<6;i++) (res+=pre[i]*calc[i])%=mod;
		return res;
	}
} using namespace Crt;

namespace Matrix{
	struct matrix{
		int s[50][50];
		void clr(){ memset(s,0,sizeof(s)); }
		void pre(){ clr(); for(int i=0;i<50;i++) s[i][i]=1; }
		void print(){for(int i=0;i<50;++i)for(int j=0;j<50;++j)write(s[i][j],j==49?'\n':' ');}
	}mat[6],base[6];
	matrix mul(matrix x,matrix y,int z){
		matrix res; res.clr();
		for(int i=0;i<50;i++)
			for(int k=0;k<50;k++)
				for(int j=0;j<50;j++)
					(res.s[i][j]+=x.s[i][k]*y.s[k][j]%z)%=z;
		return res;
	}
	void qpow(matrix& a,matrix b,int c,int d){
		while(c){
			if(c&1) a=mul(a,b,d);
			b=mul(b,b,d);
			c>>=1;
		}
	}
	void prework(){
		for(int i=1;i<6;i++){
			for(int j=0;j<pri[i];j++) mat[i].s[1][j]=bell[j]%pri[i];
			base[i].pre();
			base[i].s[1][0]=base[i].s[0][pri[i]-1]=base[i].s[1][pri[i]-1]=1;
			for(int j=1;j<pri[i]-1;j++) base[i].s[j+1][j]=1;
		}
	}
} using namespace Matrix;

signed main(){
	FILE *R=freopen("bell.in","r",stdin);
	FILE *W=freopen("bell.out","w",stdout);
	T=read(); init();
	while(T--){
		n=read(); prework();
		for(int i=1;i<6;i++){
			qpow(mat[i],base[i],n/pri[i],pri[i]);
			calc[i]=mat[i].s[1][n%pri[i]];
		}
		write(solve(),'\n');
	}
	return 0;
}

T3 穿越广场

考完后发现这好像是个AC自动机套路题,但奈何时间久远,学的时候写的太快没多思考总结,于是SB了。

\(f_{i,j,k,l}\)\(DP\)到第\(i\)位,填了\(j\)\(R\),在自动机中走到第\(k\)个节点,结束状态为\(l\)的方案数。

\(l\)为二进制状态,有两位。

于是有

\(\huge{f_{i,j,k,l} \to f_{i+1,j,to[k]['D'],l|end[to[k]['D']]}}\)

\(\huge{f_{i,j,k,l} \to f_{i+1,j+1,to[k]['R'],l|end[to[k]['R']]}}\)

初始状态\(f_{0,0,1,0}=1\)

注意每个点继承它\(fail\)的状态即可。

\(code:\)

T3
#include<bits/stdc++.h>
#define int long long
#define ULL unsigned long long
using namespace std;

namespace IO{
	inline int read(){
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	inline void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0){ putchar('-'); x=~x+1; }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	}
	inline int max(int x,int y){ return x<y?y:x; }
	inline int min(int x,int y){ return x<y?x:y; }
	inline void swap(int& x,int& y){ x^=y^=x^=y; }
	inline void ckmax(int& x,int y){ x=x<y?y:x; }
	inline void ckmin(int& x,int y){ x=x<y?x:y; }
} using namespace IO;

const int NN=110,p=1e9+7;
int t,n,m,ext,ans,f[NN<<1][NN][NN<<1][4];
char ch[NN];

namespace AC_automaton{
	int root,tot,to[NN<<1][2],fail[NN<<1],end[NN<<1];
	void insert(char *s){
		int len=strlen(s),u=1;
		for(int i=0;i<len;i++){
			int now=(s[i]=='R');
			if(!to[u][now]) to[u][now]=++tot, end[tot]=0;
			u=to[u][now];
		}
		end[u]=root?2:1;
		if(!root) root=1;
	}
	void build(){
		queue<int>q;
		if(to[root][0]) q.push(to[root][0]), fail[to[root][0]]=root;
		else to[root][0]=root;
		if(to[root][1]) q.push(to[root][1]), fail[to[root][1]]=root;
		else to[root][1]=root;
		while(!q.empty()){
			int u=q.front(); q.pop();
			end[u]|=end[fail[u]];
			if(to[u][0]) fail[to[u][0]]=to[fail[u]][0], q.push(to[u][0]);
			else to[u][0]=to[fail[u]][0];
			if(to[u][1]) fail[to[u][1]]=to[fail[u]][1], q.push(to[u][1]);
			else to[u][1]=to[fail[u]][1];
		}
	}
} using namespace AC_automaton;

signed main(){
	FILE *R=freopen("square.in","r",stdin);
	FILE *W=freopen("square.out","w",stdout);
	t=read();
	while(t--){
		m=read(); n=read(); tot=1; root=0; ext=n+m; ans=0;
		memset(f,0,sizeof(f));
		memset(to,0,sizeof(to));
		memset(fail,0,sizeof(fail));
		scanf("%s",ch); insert(ch);
		scanf("%s",ch); insert(ch);
		build(); f[0][0][1][0]=1;
		for(int i=0;i<ext;i++)
			for(int j=0;j<=m;j++){
				if(j>i||i-j>n) continue;
				for(int k=1;k<=tot;k++)
					for(int u=0;u<4;u++){
						(f[i+1][j  ][to[k][0]][u|end[to[k][0]]]+=f[i][j][k][u])%=p;
						(f[i+1][j+1][to[k][1]][u|end[to[k][1]]]+=f[i][j][k][u])%=p;
					}
			}
		for(int i=1;i<=tot;i++) (ans+=f[ext][m][i][3])%=p;
		write(ans,'\n');
	}
	return 0;
}

T4 舞动的夜晚

先跑最大流,然后在残量网络上跑\(tarjan\),如果边在\(SCC\)里说明它没有影响。

改的有点ex,待补

posted @ 2021-09-24 07:26  keen_z  阅读(50)  评论(0编辑  收藏  举报