[YDRG#001] 提瓦特环游记 · 云斗杯 · 七月 Golden 组模拟赛 整理分析--zhengjun

发布时间 2023-07-15 23:25:49作者: A_zjzj

link

总体评价:因为 K 了,所以好评,练一下思维蛮好的,质量不错

比赛 2.5h K 的。

#A. 诗人小 G 初进 OI 界

标准送分,输出 \(\frac{s_2-a_2}{a_1}\)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e6+10;
int n,a[N];
void read(ll &x){
	char c;
	for(x=0;!isdigit(c=getchar()););
	for(;x=x*10+c-48,isdigit(c=getchar()););
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	ll x,y;
	read(x),read(y);
	cout<<(y-a[2])/a[1];
	return 0;
}

#B. 派蒙是最好的伙伴!

\(A_i=\sum\limits_{j=1}^i a_j,B_i=\sum\limits_{j=1}^i b_j\)

左上角为 \((a,x)\),右下角为 \((b,y)\) 的矩阵中 \(1\) 的个数即为 \((A_b-A_{a-1})\times(B_y-B_{x-1})=k\)

然后枚举 \(k\) 的因数 \(A_b-A_{a-1}\)\(O(n)\) 计算答案即可通过。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=3e5+10,mod=998244353;
int n,a[N],b[N],ta[N],tb[N];
ll k,ans;
void calc(int x,int y){
	ll t1=0,t2=0;
	for(int i=0;i+x<=a[n];i++)t1+=1ll*ta[i]*ta[i+x];
	for(int i=0;i+y<=b[n];i++)t2+=1ll*tb[i]*tb[i+y];
	ans+=(t1%mod)*(t2%mod)%mod;
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]+=a[i-1];
	for(int i=1;i<=n;i++)scanf("%d",&b[i]),b[i]+=b[i-1];
	for(int i=0;i<=n;i++)ta[a[i]]++,tb[b[i]]++;
	for(ll i=1;i*i<=k;i++)if(k%i==0){
		if(max(i,k/i)>n)continue;
		calc(i,k/i);
		if(i*i<k)calc(k/i,i);
	}
	cout<<ans%mod;
	return 0;
}

#C. 层岩巨渊中的最长环

构造+有但不多的思维

首先考虑对于一个质数 \(p\)(接下来的 \(p\) 都表示质数),若 \(p\times 3>n\),那么 \(p\) 的度数为 \(1\),一定不能成为环上的点。

所以考虑所有满足 \(p|i,p\times 3\le n\)\(i\)

对于所有 \(p>3\),构造 \(p\times 2\to p\times k\to \cdots \to p\times k'\to p\times 3\)

然后拼接所有的这样的链:

\[p\times 2\to p\times k\to \cdots \to p\times k'\to p\times 3\to\\ p'\times 3\to p'\times k\to \cdots \to p'\times k'\to p'\times 2\to\\ p''\times 2\to p''\times k\to \cdots \to p''\times k'\to p''\times 3\to\\ \cdots \]

最后考虑所有的 \(2k,3k\),一共三段,第一段至少有一段可以和 \(2k\)\(3k\) 链连接,剩下两个连接点用 \(6,12\) 连接即可。

要求 \(n\ge 12\),所以特判所有 \(n<12\)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=5e5+10;
int T,n;
int cnt,pri[N],vis[N];
void init(int n=5e5){
	for(int i=2;i<=n;i++){
		if(!vis[i])pri[++cnt]=i;
		for(int j=1;j<=cnt&&i*pri[j]<=n;j++){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)break;
		}
	}
}
int k,ans[N];
void get(){
	scanf("%d",&n),cerr<<n<<':';
	fill(vis,vis+1+n,0);
	if(n<=12){
		if(n<=3)puts("0");
		else if(n<=5)puts("2 2 4");
		else if(n<=7)puts("3 2 4 6");
		else if(n<=9)puts("4 2 4 6 8");
		else if(n<=11)puts("5 2 4 6 8 10");
		else puts("8 2 4 8 10 6 3 9 12");
		return;
	}
	k=0;
	for(int i=3;i<=cnt&&pri[i]*3<=n;i++){
		int tag=ans[k]%2==0;
		if(tag)ans[++k]=pri[i]*2;
		else ans[++k]=pri[i]*3;
		assert(!vis[pri[i]]*2);
		assert(!vis[pri[i]]*3);
		vis[pri[i]*2]=vis[pri[i]*3]=1;
		for(int j=pri[i];j<=n;j+=pri[i]){
			if(!vis[j])ans[++k]=j,vis[j]=1;
		}
		if(tag)ans[++k]=pri[i]*3;
		else ans[++k]=pri[i]*2;
	}
	if(ans[k]%3==0){
		for(int i=3;i<=n;i+=3)if(i!=6&&i!=12){
			if(!vis[i])ans[++k]=i,vis[i]=1;
		}
		ans[++k]=6,vis[6]=1;
		for(int i=2;i<=n;i+=2)if(i^12){
			if(!vis[i])ans[++k]=i,vis[i]=1;
		}
		ans[++k]=12,vis[12]=1;
	}else{
		for(int i=2;i<=n;i+=2)if(i!=6&&i!=12){
			if(!vis[i])ans[++k]=i,vis[i]=1;
		}
		ans[++k]=6,vis[6]=1;
		for(int i=3;i<=n;i+=3)if(i^12){
			if(!vis[i])ans[++k]=i,vis[i]=1;
		}
		ans[++k]=12,vis[12]=1;
	}
	printf("%d",k);
	for(int i=1;i<=k;i++)printf(" %d",ans[i]);
	puts("");
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	for(init(),scanf("%d",&T);T--;)get();
	return 0;
}

#D. 于是他的杠精开始了

数学题+思维,设:

\[p=\gcd(a,b),q=\gcd(c,d),A=\frac{a}{p},B=\frac{b}{p},C=\frac{c}{q},D=\frac{d}{q} \]

代入原式,得到:

\[\frac{B^2-A^2}{p^2A^2B^2}=\frac{D^2-C^2}{q^2C^2D^2} \]

不管分子分母是否整除,直接令分子分母各自相等:

\[\left\{ \begin{array}{**lr**} B^2-A^2=D^2-C^2\\ pAB=qCD\\ \gcd(p,q)=1 \end{array} \right. \]

这样令 \(A,B,C,D\in [1,2\times 10^3]\)

每个 \(B^2-A^2\) 开个 vector,枚举 \(B^2-A^2\)

然后枚举一对 \((A,B)\)\((C,D)\),可以直接计算 \(p,q\) 验证一下 \(B\times p\le C\times q\) 即可。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e3+10,V=4e6+10;
int n=2e3,m=4e6,cnt;
struct zj{
	int A,B;
};
vector<zj>p[V];
int main(){
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
	cin>>cnt;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)if(__gcd(i,j)==1)
			p[j*j-i*i].push_back({i,j});
	for(int i=1;i<=m&&cnt;i++){
//		cerr<<p[i].size()<<endl;
		int len=p[i].size();
		for(int x=0;x<len&&cnt;x++){
			for(int y=0;y<x&&cnt;y++){
				int A=p[i][x].A,B=p[i][x].B;
				int C=p[i][y].A,D=p[i][y].B;
				int AB=A*B,CD=C*D;
				int g=__gcd(AB,CD);
				int p=CD/g,q=AB/g;
				ll a=1ll*A*p,b=1ll*B*p,c=1ll*C*q,d=1ll*D*q;
				if(b>c)continue;
				printf("%lld %lld %lld %lld\n",a,b,c,d);
				cnt--;
			}
		}
	}
	cerr<<cnt;
	return 0;
}

#E. 来自璃月的生日礼物

计数题,应该是本题的压轴了。

首先转化条件:

\[\sum a=\sum b=n\\ \forall i\in[1,n],\sum\limits_{j=1}^i a_j\ge \sum\limits_{j=1}^i b_j \]

写了个 \(O(n^5)\) 的 dp,前缀和优化到 \(O(n^3)\),发现难以进一步优化。

考虑换个视角。

使用挡板法,\(n-1\) 个空中放入 \(m-1\) 个挡板。

这一步是解题的关键。

要求 \(a\) 中第 \(i\) 个挡板在 \(b\) 中第 \(i\) 个挡板后面。

发现每个空只有四种状态:

  1. \(a,b\) 都有挡板

  2. \(a,b\) 都没有挡板

  3. \(a\) 有挡板

  4. \(b\) 有挡板

发现 1,2 都不影响要求的限制,且 \(3,4\) 的情况数相同。

仔细分析一下,限制可以转化为 \(4,3\) 按顺序组成括号序列即可 \(4\to (,3\to )\)

所以枚举 \(3,4\) 的个数 \(i\),计算答案:

\[ans=\sum\limits_{i=0}^{m-1}\binom{n-1}{2i}\times f_i\times \binom{n-1-i}{m-1-i} \]

其中 \(f\) 为卡特兰数。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=5e6+10,mod=1e9+7;
int n,m;
int fac[N*2],ifac[N*2];
ll qpow(ll x,ll y=mod-2){
	ll ans=1;
	for(;y;(x*=x)%=mod,y>>=1)if(y&1)(ans*=x)%=mod;
	return ans;
}
void init(int n=::n*2){
	for(int i=fac[0]=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
	ifac[n]=qpow(fac[n]);
	for(int i=n;i>=1;i--)ifac[i-1]=1ll*ifac[i]*i%mod;
}
int C(int n,int m){
	if(0>m||m>n)return 0;
	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int catalan(int n){
	return (C(n*2,n)-C(n*2,n-1)+mod)%mod;
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin>>n>>m;
	if(m>n)return puts("0"),0;
	init();
	int ans=0;
	for(int i=0;i<m;i++){
		ans=(ans+1ll*C(n-1,i*2)*catalan(i)%mod*C(n-1-i*2,m-1-i))%mod;
	}
	cout<<ans;
	return 0;
}

#F. 有根树上求八维偏序

7min 秒掉,因为法老当天下午刚刚讲过 meet in middle。

然后直接拆成 4+4,meet in middle 就做完了。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=2e5+10,K=8;
int n,a[N][K];
char str[10];
vector<int>to[N];
int cnt[5][5][5][5][5][5][5][5];
ll ans;
void dfs(int u){
	for(int i=0;i<a[u][0];i++){
		for(int j=0;j<a[u][1];j++){
			for(int k=0;k<a[u][2];k++){
				for(int x=0;x<a[u][3];x++){
					ans+=cnt[i][j][k][x][a[u][4]][a[u][5]][a[u][6]][a[u][7]];
				}
			}
		}
	}
	for(int i=a[u][4]+1;i<5;i++){
		for(int j=a[u][5]+1;j<5;j++){
			for(int k=a[u][6]+1;k<5;k++){
				for(int x=a[u][7]+1;x<5;x++){
					cnt[a[u][0]][a[u][1]][a[u][2]][a[u][3]][i][j][k][x]++;
				}
			}
		}
	}
	for(int v:to[u])dfs(v);
	for(int i=a[u][4]+1;i<5;i++){
		for(int j=a[u][5]+1;j<5;j++){
			for(int k=a[u][6]+1;k<5;k++){
				for(int x=a[u][7]+1;x<5;x++){
					cnt[a[u][0]][a[u][1]][a[u][2]][a[u][3]][i][j][k][x]--;
				}
			}
		}
	}
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",str);
		for(int j=0;j<K;j++)a[i][j]=str[j]-'0'-1;
	}
	for(int i=2,x;i<=n;i++){
		scanf("%d",&x),to[x].push_back(i);
	}
	dfs(1);
	cout<<ans;
	return 0;
}