Preface
昨天睡得有点晚因此今天脑子不是很清醒……
T1本来一眼秒了的,结果自己数\(n=4\)的个数的时候输错了就以为自己错了
后来写了个假算法发现全场就我没过T1:(
A. 「NOIP2022模拟赛二 By ZJ A」数正方形
Pro
- 在坐标系内放置\(n\)个边长相等(由你决定)的正方形(边必须与\(x,y\)轴平行)
- 求一种正方形数目尽量多的方案,输出边长\(k\)以及左下角的坐标
- \(n\le 1314\)
Sol
其实直接让\(k=n\)然后\((x_i,y_i)=i,i\)即可
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
int n;
int main()
{
scanf("%d",&n); printf("%d\n",n);
for (RI i=0;i<n;++i) printf("%d %d\n",i,i);
return 0;
}
B. 「NOIP2022模拟赛二 By ZJ B」英语作文
Pro(稍作修改)
- 有\(n\)个数字字符串\(s_i\),现在要选出一些将它们顺次连接在一起
- 求最后字符串总长度不超过\(D\)时最大的数是多少
- \(D\le2\times10^2,n\le10^4,|s_i|\le 32\)
Sol
如果没有那个位数限制,我们发现直接贪心,定义\(A\)排在\(B\)前面的条件为\(A+B>B+A\)(\(+\)表示连接两个字符串),然后从前往后把所有的字符串连起来即可
那么现在多了一个长度限制怎么办,其实就是一个类似于\(0/1\)背包的东西,因为不管我们最后选了哪些串都一定是从前往后顺序连接起来的
直接做即可(话说为什么人家都是200+ms我直接2000+ms……)
#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#define RI int
#define CI const int&
using namespace std;
const int N=10005,M=205;
int n,m; string s,f[M],a[N],ans;
inline char trs(const char& ch)
{
switch (ch)
{
case 'O': return '0';
case 'D': return '0';
case 'G': return '9';
case 'B': return '8';
case 'L': return '7';
case 'q': return '6';
case 'S': return '5';
case 'h': return '4';
case 'E': return '3';
case 'Z': return '2';
case 'I': return '1';
};
}
inline bool cmp(const string& A,const string& B)
{
return A+B>B+A;
}
inline bool check(const string& A,const string& B)
{
int t1=0,t2=0; while (A[t1]=='0') ++t1; while (B[t2]=='0') ++t2;
if (A.length()-t1!=B.length()-t2) return A.length()-t1>B.length()-t2;
return A>B;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; std::ios::sync_with_stdio(false); cin>>m>>n;
for (i=1;i<=n;++i) for (cin>>s,j=s.length()-1;~j;--j) a[i]+=trs(s[j]);
for (sort(a+1,a+n+1,cmp),i=1;i<=n;++i) for (j=m;j>=a[i].length();--j)
if (check(f[j-a[i].length()]+a[i],f[j])) f[j]=f[j-a[i].length()]+a[i];
//for (i=1;i<=n;++i) cout<<i<<' '<<a[i]<<'\n';
//for (i=0;i<=m;++i) cout<<i<<' '<<f[i]<<'\n';
for (ans=f[0],i=1;i<=m;++i) if (check(f[i],ans)) ans=f[i];
if (ans[0]!='0') return cout<<ans,0; cout<<"0.";
for (ans=f[0],i=1;i<=m;++i) if (f[i]>ans) ans=f[i];
for (i=1;i<ans.length();++i) cout<<ans[i]; return 0;
}
C. 「NOIP2022模拟赛二 By ZJ C」等比数列
Pro
- 有一个长度为\(n\)的数列\(a=\{1,2,3,\cdots,n\}\)
- 求出\(a\)有多少个长度至少为\(3\)的子序列是等比数列
- 数据组数\(T\le 10^6,n\le 10^{14},\sum \sqrt n\le 10^7\),答案对\(998244353\)取模
Sol
我是nt,竟然一点都不会做的说
首先考虑对于一个长度为\(k+1\),首项为\(A\),公比为\(\frac{p}{q},\gcd(p,q)=1\)的等比数列是唯一确定的
此时其每一项的表达式为\(A\cdot \frac{p^i}{q^i}(i\in [0,k])\),不难发现如果要让所有数为整数只要末项\(A\cdot \frac{p^k}{q^k}\)为整数即可,即\(q^k|A\)
我们可以直接暴枚\(q\)和\(k\),\(p\)的取值就是\(\phi(q)\)种,然后再乘上\(A\)的取值数目$\lfloor \frac{n}{q^k}\rfloor $即可
复杂度不知道怎么算,总之大概是\(\sqrt n\)级别的
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int S=10000005,mod=998244353;
int t,pri[S],phi[S],cnt,ans; bool vis[S]; long long n; __int128 pw;
inline void init(CI n)
{
RI i,j; for (vis[1]=phi[1]=1,i=2;i<=n;++i)
{
if (!vis[i]) pri[++cnt]=i,phi[i]=i-1;
for (j=1;j<=cnt&&i*pri[j]<=n;++j)
if (vis[i*pri[j]]=1,i%pri[j]) phi[i*pri[j]]=phi[i]*(pri[j]-1);
else { phi[i*pri[j]]=phi[i]*pri[j]; break; }
}
}
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
for (init(10000000),scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%lld",&n),ans=0,i=2;1LL*i*i<=n;++i)
for (pw=1LL*i*i,j=2;pw<=n;++j,pw*=i) inc(ans,1LL*phi[i]*(n/pw)%mod);
printf("%d\n",ans);
}
}
D. 「NOIP2022模拟赛二 By ZJ D」沙漏求值
Pro
- 有\(n\)个沙漏,每个沙漏有一个参数\(a_i\),它们按编号从上到下放置
- 这个沙漏漏沙子的规律是这样的:如果到了\(i\)号漏斗时有\(s\)颗沙子,则会向下漏出\(x_i=s\bmod a_i\)颗沙子
- 现在你可以在最上面的沙漏中放任意颗沙子,求\(\max\{\sum_{i=1}^n x_i\}\)
- \(n\le 2\times 10^5,a_i\le 10^{18}\)
Sol
首先考虑暴力DP,设\(f_{i,j}\)表示前\(i\)个数,当前\(x_i=j\)时最大的\(\sum_{k=1}^i x_k\),这样可以做到\(O(n\cdot a_i)\)的复杂度
但是显然这个DP没法再优化了,因为状态本身数量级就爆了而且每种状态都有可能有贡献
那么考虑修改状态,不难发现\(x_j\le x_i,(j>i)\),同时若\(x_i\ne 0\),那么把初始值全减\(1\)一定可以让\(x_i\)减\(1\)
考虑这样设计状态,令\(y=\sum_{j=1}^i (x_j-x_i)\),那么如果我们把初始值\(S\)变为\(S-x_i+k(k\in[0,x_i))\)一定有\(\sum_{j=1}^i x_j=k\cdot i+y\)
因此我们考虑这样设计状态,令\(f_{i,j}\)表示前\(i\)个数,当\(x_i=j\)时最大的\(y\),考虑转移
先假定每次枚举\(k\),那么就有\(f_{i+1,k\ \bmod\ a_{i+1}}\leftarrow f_{i,j}+(k-k\bmod a_{i+1})\times i\),但我们显然是不可能每次枚举\(k\)的
先设\(j\ge a_{i+1}\),考虑\(k\bmod a_{i+1}\)的取值只有\(a_{i+1}\)种,不妨把\([0,j]\bmod a_{i+1}\)的取值写成一排,它们显然是一段带周期的\([0,a_{i+1})\)和一些零头
我们发现最优的转移点只有两个,一个是\(j\),一个就是最后一个周期的\(a_{i+1}-1\)
\(j<a_{i+1}\)的情况就更加简单了,直接保留即可
考虑这样DP的复杂度,显然我们可以保留数值来优化掉第一维,第二维用map
维护即可
由于取模操作可以让一个数至少减半,因此每个\(j\)最多转移\(\log a_i\)次,总复杂度就是\(O(n\log n\log a_i)\)
#include<cstdio>
#include<map>
#define int __int128
#define RI register int
#define CI const int&
const int N=200005;
using namespace std;
int n,a[N],ans; map <int,int> f;
inline void print(int x)
{
if (x>9) print(x/10); putchar(x%10+'0');
}
signed main()
{
RI i; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]);
for (f[a[1]-1]=0,i=2;i<=n;++i)
{
for (auto it=f.lower_bound(a[i]);it!=f.end();++it)
{
int x=it->first,y=it->second;
f[x%a[i]]=max(f[x%a[i]],y+(i-1)*(x-x%a[i]));
f[a[i]-1]=max(f[a[i]-1],y+(i-1)*(((x+1)/a[i]-1)*a[i]));
}
f.erase(f.lower_bound(a[i]),f.end());
}
for (auto it:f) ans=max(ans,it.second+n*it.first);
return print(ans),0;
}
Postscript
接下来应该就不来了,好好准备迎接大学生活吧
标签:case,return,ZJ,int,8.20,NOIP2022,const,RI,define From: https://www.cnblogs.com/cjjsb/p/16607770.html