NOIP2025模拟赛26
T1 | T2 | T3 | T4 |
---|---|---|---|
\(\color{#52C41A} 普及+/提高\) | \(\color{#3498DB} 提高+/省选-\) | \(\color{#9D3DCF} 省选/NOI-\) | \(\color{#9D3DCF} 省选/NOI-\) |
参赛网址:https://oj.33dai.cn/d/TYOI/contest/68a3e478c5d9c2f14c26c089
这场我的评价是,这场又是 education 场,所以听题解的时候务必认真听哦。
T2,T4未搭建完成
T1 代数几何[2022NOIP模拟赛T1(合并DIV)]
题目难度:\(\color{#52C41A} 普及+/提高\)
算法标签:贪心,其他,构造
思路
我们发现这个题相当于将若干个数分成一个环,然后找环内的乘积最大值。
例:\(n=8,k=2\) 就是将 \(a_0,a_2,a_4,a_6\) 分为一个环,剩下的在另一个组。
然后发现 \(a_0\) 所在的环分别是:\(a_0,a_{0+k},a_{0+2\times k} \dots a_{0+(\alpha-1)\times k}\)
当 \(0+\alpha\times k \equiv0 \pmod n\) .
然后我们考虑一个特殊情况:
\(\gcd(n,k)=1\),即 \(n\) 和 \(k\) 互质时,\(\alpha\equiv0\pmod n\)。
那么我们就可以考虑,设 \(g=\gcd(n,k)\),则 \(k=\beta \times g\),所以 \(0+\alpha\times \beta \times g \equiv0 \pmod n\).
\(\alpha \times g \equiv0 \pmod n\)
\(\alpha = \frac n g\)
所以0所在的环的长度为 \(\frac n {\gcd(n,k)}\)
然后我们发现 \(1+\alpha\times \beta \times g \equiv1 \pmod n\).
所以1所在的环的长度也是 \(\frac n {\gcd(n,k)}\)。
然后我们发现一共有 \(\gcd(n,k)\) 个环,每个环的大小为\(\frac n {\gcd(n,k)}\)。
特别感谢@Sunpicnic对笔者数学证明方面的帮助,orz.
然后贪心,使用中华传统文化。
我们贪心的去找答案,为了使答案最大,就是最大的数贡献最大,设 \(a\) 是最大值,\(b\) 是次大值,\(c\) 是第三大值,则应 \(ans_{i}=a\),\(ans_{(i+k) mod n}=b\),\(ans_{(i-k+n)mod n}=c\)。
AC Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5005;
int n,m,k;
int a[maxn];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
cin>>m;
while (m--){
int ans=0;
cin>>k;
int g=__gcd(n,k);
int len=n/g;
for (int i=1;i+len-1<=n;i+=len){
int l=i,r=i+len-1;
int hd=a[l],tl=a[l];
for (int j=l+1;j<=r;j++){
if (j%2==1) ans+=hd*a[j],hd=a[j];
else ans+=tl*a[j],tl=a[j];
}
ans+=hd*tl;
}
cout<<ans<<"\n";
}
return 0;
}
T2 找环[2022NOIP模拟赛T2(合并DIV)]
题目难度:\(\color{#3498DB} 提高+/省选-\)
算法标签:贪心,倍增,二分
思路
AC Code
T3 维护数组[2022NOIP模拟赛T3(合并DIV)]
题目难度:\(\color{#9D3DCF} 省选/NOI-\)
算法标签:其他,分块,数据结构,线段树,数学
思路
30分:太难了,我不会
https://oj.33dai.cn/d/TYOI/p/N0055/solution
做法1:分块
对于每一块,考虑维护\(a_i\),\(p_i\),\(mx\),\(ans\),分别为原数组,\(max_{j=1}^i a_i\),整块最大值,这个序列的答案。
设块长为 \(b\),则对于每一次的修改操作,直接扫一遍即可,时间复杂度 \(O(b)\)。
对于每一次查询:
-
对于第一个块,对答案的贡献为 \(ans_i\)。
-
对于剩下的块,如果这个块的 \(mx_i \le\) 前面的所有值的最大值,则对答案的贡献为 \(b\times\) 前面的所有值的最大值,否则二分找到小于前面的所有值的最大值,暴力。
最坏时间复杂度 \(O(\frac b n \times log_2 b)\)
总时间复杂度 \(O((\frac b n \times log_2 b+b)m)\)
当 \(b\) 取800左右的时候比较快。
做法2:兔队线段树
做法3:线段树
AC Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=3e5+5;
int n,Q;
int pos,val;
int siz,cnt;
int a[maxn];
int id[maxn],l[maxn],r[maxn];
int p[maxn],ans[maxn];
void update(int x,int k){
a[x]=k;
p[l[id[x]]]=a[l[id[x]]];
ans[l[id[x]]]=p[l[id[x]]];
for (int i=l[id[x]]+1;i<=r[id[x]];i++){
p[i]=max(p[i-1],a[i]);
ans[i]=ans[i-1]+p[i];
}
}
int query(){
int res=0,mx=0;
for (int i=1;i<=cnt;i++){
if (p[r[i]]<=mx) res+=(r[i]-l[i]+1)*mx;
else if (p[l[i]]>=mx) res+=ans[r[i]];
else {
int x=l[i],y=r[i],po=0;
while (x<=y){
int mid=((x+y)>>1);
if (p[mid]<mx){
po=mid;
x=mid+1;
}
else y=mid-1;
}
if (po>=1) res+=(po-l[i]+1)*mx;
res+=(ans[r[i]]-ans[po]);
}
mx=max(mx,p[r[i]]);
}
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
siz=800;
cnt=n/siz+(n%siz!=0);
for (int i=1;i<=n;i++){
cin>>a[i];
id[i]=(i-1)/siz+1;
}
for (int i=1;i<=cnt;i++){
l[i]=(i-1)*siz+1;
r[i]=i*siz;
}
r[cnt]=n;
for (int i=1;i<=cnt;i++){
p[l[i]]=a[i];
ans[l[i]]=a[i];
for (int j=l[i]+1;j<=r[i];j++)
p[j]=max(p[j-1],a[j]),
ans[j]=ans[j-1]+p[j];
}
cin>>Q;
while (Q--){
cin>>pos>>val;
update(pos,val);
cout<<query()<<"\n";
}
return 0;
}
T4 水坑[2022NOIP模拟赛T4(合并DIV)]
题目难度:\(\color{#9D3DCF} 省选/NOI-\)
算法标签:重构树,DP,DFS