2024-04-04

2024-04-04

gcd

上午模拟赛 T1

考场上写了 \(O(n^2)\) 的暴力,但是有的时候跑不满,\(10^5\) 的大数据跑的飞快(100 ms)

考完没多久就想出来正解了

观察到值域 \(V\) 只有 \(100\),考虑把他放到时间复杂度里面

枚举最大公约数 \(g\)

只有一段区间内所有的数都是 \(g\) 的倍数的时候才有可能区间最大公约数是 \(g\)

我们发现整个序列被不是 \(g\) 的倍数的数切成了若干部分

我们只需要考虑 极长的 所有数都是 \(g\) 的倍数的区间

因为当数的个数增加时,\(\gcd\) 单调不增

所以非极长序列会在 \(g\) 更大的时候被考虑到

求区间 \(\gcd\) 可以用 st表 实现

时间复杂度 \(O(n\log n+nV)\)

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1e6+100;

int n;
int st[N][20];
int lg[N];

int gcd(int x,int y) {
	return y==0?x:gcd(y,x%y);
}

bool check(int l,int r,int g) {
	int d=lg[r-l+1];
	return gcd(st[l][d],st[r-(1<<d)+1][d])==g;
}

int main() {
	scanf("%d",&n);
	for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++) scanf("%d",&st[i][0]);
	for(int j=1;j<=lg[n];j++) {
		for(int i=1;i<=n;i++) st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	}
	int ans=0;
	for(int g=1;g<=100;g++) {
		int las=0;
		bool flg=false;
		for(int i=1;i<=n;i++) {
			if(st[i][0]%g!=0) {
				if(i-1>=las+1) flg|=check(las+1,i-1,g);
				las=i;
			}
		}
		if(n>=las+1) flg|=check(las+1,n,g);
		ans+=flg;
	}
	printf("%d\n",ans);
	
	return 0;
} 

turn

懂了但是总结写着太费时间了,直接把题解粘上来吧

组合数有关问题经常转化为平面上满足某些限制的路径条数
其中不碰到某条直线的可以转化为所有路径减去碰到的条数
碰到某条线的条数又可以通过对称转化为两点之间路径条数

\(fact\) 的逆元 \(invf\)(用于求组合数)可以递推求得:

  • 首先求出 \(invf_n\) (费马小定理+快速幂)
  • 假设已知 \(invf_k\),由于 \(fact_k\times invf_k\equiv1\pmod{p}\),所以 \(fact_{k-1}\times k\times invf_k\equiv1\pmod{p}\),得到 \(invf_{k-1}=invf_k\times k\)
  • 从后往前递推即可
#include<iostream>
#include<cstring>
#include<algorithm>

#define mod 998244353

using namespace std;

typedef long long ll;

const int N=1e7+10;

int n,m;
ll a,b;

ll fact[N],invf[N];

ll qpow(ll a,ll k,ll p) {
	ll res=1;
	while(k) {
		if(k&1) res=res*a%p; 
		a=a*a%p;
		k>>=1;
	}
	return res;
}

ll C(int x,int y) {
	if(x<y||x==0) return 0;
	return fact[x]*invf[y]%mod*invf[x-y]%mod;
}

ll pa,pb;

ll G(int x,int y) {
	int A=C(x+y-1,y),B=C(x+y-1,y-1);
	return ((A-B)%mod+mod)%mod*pa%mod*pb%mod;
}

int main() {
	fact[0]=1;
	for(ll i=1;i<=10000000;i++) fact[i]=fact[i-1]*i%mod;
	invf[10000000]=qpow(fact[10000000],mod-2,mod);
	for(ll i=10000000;i>=0;i--) invf[i-1]=invf[i]*i%mod;
	scanf("%d%d%lld%lld",&n,&m,&a,&b);
	ll s=qpow(a+b,mod-2,mod);
	a=a*s%mod,b=b*s%mod;
	ll ans=0;
	pa=1,pb=qpow(b,n,mod);
	for(int x=n;x<=(m+n)/2;x++) {
		int y=x-n;
		ans=(ans+G(x,y))%mod;
		pa=pa*a%mod,pb=pb*b%mod;
	}
	printf("%lld\n",ans);
	
	return 0;
}

matrix

accoders 真是答辩,数组开大了也能 segmentation fault ,就非得卡着开

\(N=n\times m\)
\(m\le n\) 所以 \(m\le\sqrt{n}\)

枚举矩形的左右边界

\[Ans=\sum_{i=0}^{n-1}\sum_{j=i+1}^n\left(S_j-S_i\right)^k \]

用二项式定理展开

\[Ans=\sum_{p=0}^k\sum_{i=0}^{n-1}\sum_{j=i+1}^n{S_j}^p\times(-1)^{k-p}{S_i}^{k-p}\times {k\choose p} \]

发现和 \(j\) 有关的部分是前缀和的后缀和,可以预处理

总的时间复杂度为 \(O(m^2nk)=O(N\sqrt Nk)\)

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

typedef unsigned long long ull;

const int N=1e5+1e4+1000,M=400,K=15;

int n,m,k;
ull C[K][K];

ull read() {
	ull x=0;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x;
}

int main() {
	C[0][0]=1;
	for(int i=1;i<=12;i++) {
		C[i][0]=1;
		for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
	scanf("%d%d%d",&n,&m,&k);
	ull line[n+3][m+2],sum[n+3][K][2],sfx[n+3][K];
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) line[i][j]=read(),line[i][j]+=line[i][j-1];
	ull ans=0;
	for(int lft=1;lft<=m;lft++) {
		for(int rgh=lft;rgh<=m;rgh++) {
			memset(sum[0],0,sizeof(sum[0]));
			memset(sfx[n+1],0,sizeof(sfx[n+1]));
			for(int i=0;i<=n;i++) sum[i][0][0]=sum[i][0][1]=1;
			for(int i=1;i<=n;i++) sum[i][1][0]=sum[i-1][1][0]+line[i][rgh]-line[i][lft-1],sum[i][1][1]=-sum[i][1][0];
			for(int i=1;i<=n;i++) {
				for(int p=2;p<=k;p++)
					sum[i][p][0]=sum[i][p-1][0]*sum[i][1][0],sum[i][p][1]=sum[i][p-1][1]*sum[i][1][1];
			}
			for(int i=n;i>=1;i--) {
				for(int p=0;p<=k;p++)
					sfx[i][p]=sfx[i+1][p]+sum[i][p][0];
			}
			for(int p=0;p<=k;p++) {
				ull val=0;
				for(int i=0;i<n;i++)
					val+=sfx[i+1][p]*sum[i][k-p][1];
				ans+=val*C[k][p];
			}
		}
	}
	cout<<ans<<endl;
	
	return 0;
}
posted @ 2024-04-04 21:39  OrangeStar*  阅读(13)  评论(0编辑  收藏  举报