[YDRG#001] 提瓦特环游记 · 云斗杯 · 七月 Golden 组模拟赛 整理分析--zhengjun
总体评价:
因为 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\)。
然后拼接所有的这样的链:
最后考虑所有的 \(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. 于是他的杠精开始了
数学题+思维,设:
代入原式,得到:
不管分子分母是否整除,直接令分子分母各自相等:
这样令 \(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. 来自璃月的生日礼物
计数题,应该是本题的压轴了。
首先转化条件:
写了个 \(O(n^5)\) 的 dp,前缀和优化到 \(O(n^3)\),发现难以进一步优化。
考虑换个视角。
使用挡板法,\(n-1\) 个空中放入 \(m-1\) 个挡板。
这一步是解题的关键。
要求 \(a\) 中第 \(i\) 个挡板在 \(b\) 中第 \(i\) 个挡板后面。
发现每个空只有四种状态:
-
\(a,b\) 都有挡板
-
\(a,b\) 都没有挡板
-
仅 \(a\) 有挡板
-
仅 \(b\) 有挡板
发现 1,2 都不影响要求的限制,且 \(3,4\) 的情况数相同。
仔细分析一下,限制可以转化为 \(4,3\) 按顺序组成括号序列即可 \(4\to (,3\to )\)。
所以枚举 \(3,4\) 的个数 \(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;
}