Comet OJ - 模拟赛 #2 Day1

正题

      Portal

      cometoj出的题质量还是蛮高的,至少让我这种菜鸡选手找到了思考的乐趣。

      T1

      这题很水,因为要找波浪,所以用a1,a2分别记录以i结尾的降波浪和升波浪的种数,每次交换一下把其中的一边清空为1就可以了。

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

long long ans=0;
int a1,a2,n,las,x;

int main(){
	scanf("%d",&n);
	scanf("%d",&las);
	a1=a2=1;
	for(int i=2;i<=n;i++){
		scanf("%d",&x);
		if(las<=x) a2=a1+1,a1=1;
		else a1=a2+1,a2=1;
		ans+=a1+a2-2;las=x;
	}
	printf("%lld",ans);
}

      T2

      一眼下去直接写了一个扩展欧几里得找非负整数解的个数,看了看题发现不对,这个还有顺序,想着构造多项式想了半天,不太实际。

       发现有一个max(a,b)\log_2 n的矩阵快速幂做法,但是这个是齐次线性递推式,所以可以用特征多项式做到max(a,b)\log_2max(a,b)\log_2n,然后有一个n/\gcd(a,b)的做法,就是把\gcd(a,b)的倍数提出来递推。又有一个n/lcm(a,b)的做法,这个就是直接把ax+by=n的所有解(x,y)找出来,每一组解的贡献就是C_{x+y}^x,考虑到x+y可能很大,但是模数很小,所以直接上Lucas.

       那么把做法拼在一起,就可以拿到全部的分数。

       比赛的时候忘记判无解少了30分,OJ还炸了。

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

const int N=110;
long long n,a,b;
int T;
const long long mod=10007;
struct Matrix{
	int g[N][N];
	friend Matrix operator*(const Matrix&A,const Matrix&B){
		Matrix G;
		for(int i=1;i<=T;i++)
			for(int j=1;j<=T;j++){
				G.g[i][j]=0;
				for(int k=1;k<=T;k++)
					G.g[i][j]+=A.g[i][k]*B.g[k][j]%mod;
				G.g[i][j]%=mod;
			}
		return G;
	}
}TOT,X;
int fac[mod],inv[mod];

int ksm(int x,int t){
	int tot=1;
	while(t){
		if(t&1) (tot*=x)%=mod;
		(x*=x)%=mod;t/=2;
	}
	return tot;
}

void solve1(){
	T=max(a,b);
	for(int i=1;i<=T;i++) TOT.g[i][i]=1;
	X.g[a][1]++,X.g[b][1]++;
	for(int i=1;i<=T-1;i++) X.g[i][i+1]=1;
	while(n){
		if(n&1) TOT=TOT*X;
		X=X*X;n/=2;
	}
	printf("%d\n",TOT.g[1][1]);
}

int exgcd(int a,int b,long long&x,long long&y){
	if(b==0) {x=1;y=0;return a;}
	int z=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return z;
}

int C(int x,int y){
	return (x<y?0:fac[x]*inv[y]%mod*inv[x-y]%mod)%mod;
}

int Lucas(int x,int y){
	if(x==0 && y==0) return 1;
	return Lucas(x/mod,y/mod)*C(x%mod,y%mod)%mod;
}

void solve2(){
	long long x,y;
	int g=exgcd(a,b,x,y);
	x*=n/g,y*=n/g;
	if(n%g) {printf("0");return ;}
	long long La=a/g,Lb=b/g,tmp;
	if(x<0) tmp=(-x+Lb-1)/Lb,x+=tmp*Lb,y-=tmp*La;
	if(y<0) tmp=(-y+La-1)/La,y+=tmp*La,x-=tmp*Lb;
	if(x<0 || y<0) {printf("0");return ;}
	tmp=x/Lb,x-=tmp*Lb,y+=tmp*La;
	int ans=0;
	while(y>=0){
		ans+=Lucas(x+y,x);ans%=mod;
		x+=Lb,y-=La;
	}
	printf("%d\n",ans);
}

int main(){
	fac[0]=1;for(int i=1;i<mod;i++) fac[i]=fac[i-1]*i%mod;
	inv[mod-1]=ksm(fac[mod-1],mod-2);for(int i=mod-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
	scanf("%lld %lld %lld",&n,&a,&b);
	if(a<=100 && b<=100) solve1();
	else solve2();
}

      T3

      第三题是道好题,结论易证但是并不显然:联通块个数=n-两端相同颜色边数目+所有点颜色相同的简单环个数。

      证明可以首先不考虑简单环,这个是显然正确的。

      考虑给一个联通块加上一个颜色相同的简单环(假设其中有一个点已经在里面),那么边的数量等于加上的点的数量+1,所以减的就多了1,加回来即可。

      显然不存在其他情况,因此得证。

      接着的事情就很简单了,因为这个是一个点不可能存在于多个简单环的仙人掌,所以跑Tarjan条件就少了很多,另外考虑到一个点最多有两个父亲,所以直接记录下来,另外记录一下自己儿子取某种颜色的总概率,每一次O(k)更新就可以了,环的情况是相乘的,所以0的情况特殊处理一下就可以了,另外不要因为贪快而省掉许多mod导致错误哦!

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

const int N=100010;
const long long mod=998244353;
int n,m,k,q;
struct edge{
	int y,next;
}s[N<<2];
int first[N],len=0,fa1[N],fa2[N],t[N][5];
int T=0,B=0;
long long f[N][5],W[N][5],F[N][5],ans;
int sta[N],top=0,dfn[N],low[N],where[N];

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

long long ksm(long long x,long long t){
	long long tot=1;
	while(t){
		if(t&1) (tot*=x)%=mod;
		(x*=x)%=mod;t/=2;
	}
	return tot;
}

void init(int x){
	static int T=0;T=0;
	for(int j=0;j<k;j++) scanf("%d",&f[x][j]),T+=f[x][j];
	T=ksm(T,mod-2);for(int j=0;j<k;j++) f[x][j]=f[x][j]*T%mod;
}

void Tarjan(int x,int fa){
	low[x]=dfn[x]=++T;sta[++top]=x;
	for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa){
		if(dfn[s[i].y]) low[x]=min(low[x],dfn[s[i].y]);
		else Tarjan(s[i].y,x),low[x]=min(low[x],low[s[i].y]);
	}
	if(low[x]==dfn[x]){
		B++;
		for(int j=0;j<k;j++) W[B][j]=1;
		int P=0;
		while(1){
			int now=sta[top];top--;
			for(int j=0;j<k;j++) f[now][j]?W[B][j]=W[B][j]*f[now][j]%mod:t[B][j]++;
			where[now]=B;P++;
			if(now==x) break;
		}
		if(P==1) where[x]=0,B--;
	}
}

void dfs(int x){
	dfn[x]=true;
	for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa1[x]){
		if(dfn[s[i].y] && fa2[s[i].y]!=x){
			fa2[x]=s[i].y;
			for(int j=0;j<k;j++) F[s[i].y][j]+=f[x][j];
		}
		else if(!dfn[s[i].y]){
			for(int j=0;j<k;j++) F[x][j]+=f[s[i].y][j];
			fa1[s[i].y]=x;dfs(s[i].y);
		}
	}
}

int main(){
	scanf("%d %d %d",&n,&m,&k);
	int type,x,y;
	for(int i=1;i<=m;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
	for(int i=1;i<=n;i++) init(i);
	Tarjan(1,0);
	memset(dfn,0,sizeof(dfn));
	dfs(1);
	scanf("%d",&q);
	ans=n;
	for(int i=1;i<=n;i++) for(int j=0;j<k;j++) F[i][j]%=mod,ans+=mod-F[i][j]*f[i][j]%mod;
	for(int i=1;i<=B;i++) for(int j=0;j<k;j++) ans+=(t[i][j]?0:W[i][j]);ans%=mod;
	while(q--){
		scanf("%d",&type);
		if(type==1){
			scanf("%d",&x);
			if(fa1[x]) for(int j=0;j<k;j++) ans+=f[x][j]*f[fa1[x]][j]%mod,F[fa1[x]][j]+=mod-f[x][j];
			if(fa2[x]) for(int j=0;j<k;j++) ans+=f[x][j]*f[fa2[x]][j]%mod,F[fa2[x]][j]+=mod-f[x][j];
			for(int j=0;j<k;j++) ans+=f[x][j]*F[x][j]%mod;
			if(where[x]) for(int j=0;j<k;j++) ans+=mod-(t[where[x]][j]?0:W[where[x]][j]),f[x][j]?(W[where[x]][j]*=ksm(f[x][j],mod-2))%=mod:t[where[x]][j]--;
			init(x);
			if(fa1[x]) for(int j=0;j<k;j++) ans+=mod-f[x][j]*f[fa1[x]][j]%mod,(F[fa1[x]][j]+=f[x][j])%=mod;
			if(fa2[x]) for(int j=0;j<k;j++) ans+=mod-f[x][j]*f[fa2[x]][j]%mod,(F[fa2[x]][j]+=f[x][j])%=mod;
			for(int j=0;j<k;j++) ans+=mod-f[x][j]*F[x][j]%mod;
			if(where[x]) for(int j=0;j<k;j++) (f[x][j]?(W[where[x]][j]*=f[x][j])%=mod:t[where[x]][j]++),ans+=(t[where[x]][j]?0:W[where[x]][j]);
			ans%=mod;
		}
		else printf("%lld\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/103322162
今日推荐