• NOIP2022模拟赛一 By QY8.16


    Preface

    今天这场代码量好小啊……

    T4原来是经典的除法分块,但我都忘光了,远程求助陈指导才会


    A. 「NOIP2022模拟赛一 By QY A」签到题

    Pro

    • 构造一个长度为\(n\)的排列\(p\),使得其\(\max(\operatorname{lis(p),\operatorname{lds(p)}})\)是所有排列中最小的一个(相同则输出任意一种)
    • \(\operatorname{lis(p)},\operatorname{lds(p)}\)分别表示\(p\)最长上升子序列长度和最长下降子序列长度
    • \(n\le 100000\)

    Sol

    刚开始有点迷,打了个暴力找了下规律就会了

    不难证明\(\max(\operatorname{lis(p),\operatorname{lds(p)}})\)的最小值就是\(\lceil\sqrt n\rceil\),考虑以下的构造方法:

    n=9
    3 2 1 6 5 4 9 8 7
    
    n=11
    3 2 1 7 6 5 4 11 10 9 8
    

    普遍地,把序列分为一些大小为\(\sqrt n\)的块,块的内部保证连续降序,然后块的总数也是\(\sqrt n\)级别的,升序子序列只能存在与不同的块之间

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005;
    int n,a[N],m;
    int main()
    {
    	RI i,j,k; scanf("%d",&n); m=(int)sqrt(n); if (m*m<n) ++m;
    	for (i=n-m+1,k=n;i>=1;i-=m) for (j=0;j<m;++j) a[i+j]=k--;
    	for (i=1;i<=n%m;++i) a[i]=k--;
    	for (i=1;i<=n;++i) printf("%d ",a[i]);
    	return 0;
    }
    

    B. 「NOIP2022模拟赛一 By QY B」简单题

    Pro

    • 在一个圆中随机分布着\(n\)个点,求存在一条直径使得所有点均在直线一侧的概率
    • \(n\le 100000\),答案对\(998244353\)取模

    Sol

    首先在圆内和在圆周上是等价的,然后考虑先选定一个点为起点

    以这个起点为端点,规定一个方向(假定为逆时针)做得一段半圆周,我们强制接下来的点都要落在这个半圆周上

    剩下\(n-1\)个点都落在上面的概率是\(\frac{1}{2^{n-1}}\),再乘上起点选法\(n\)就是答案了

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int mod=998244353;
    int n;
    inline int quick_pow(int x,int p=mod-2,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    int main()
    {
    	return scanf("%d",&n),printf("%d",1LL*n*quick_pow(quick_pow(2,n-1))%mod),0;
    }
    

    C. 「NOIP2022模拟赛一 By QY C」数列题

    Pro

    • 有一个长度为\(n\)的数列\(a\)\(a_i\)在初始时是整数,后续过程中可以变成实数
    • 你可以对一个数\(a_i\)进行一次修改,将其值变为\(a_i'\),这要花费\((a_i-a_i')^2\)的代价
    • 求让最后的数列变为等差数列的最小代价总和
    • \(n\le 1000\),数据组数T\le 1000$

    Sol

    考虑等差数列的一般表达形式,\(d_n=A\cdot n+B\),把\(n\)看做自变量,\(d_n\)看做因变量,这其实就是一条直线的方程

    而且我们发现原来给出的值\(a_i\)可以看做在平面内的点\((i,a_i)\),而代价的定义也和SSE(残差平方和)一模一样

    因此我们直接使用线性回归的相关知识,用最小二乘法得到最优的拟合直线\(y=kx+b\),有:

    \[k=\frac{\sum_{i=1}^n (x_i-\overline x)(y_i-\overline y)}{\sum_{i=1}^n (x_i-\overline x)^2}\\ b=\overline y-k\overline x \]

    就可以轻松算出答案了,复杂度\(O(Tn)\)

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    #define Tp template <typename T>
    using namespace std;
    const int N=1005;
    int t,n,a[N]; double _x,_y,f1,f2,k,b,ans;
    class FileInputOutput
    {
    	private:
    		static const int S=1<<21;
    		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
    		char Fin[S],*A,*B;
    	public:
    		Tp inline void read(T& x)
    		{
    			x=0; char ch; bool flag=0; while (!isdigit(ch=tc())) if (ch=='-') flag=1;
    			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); if (flag) x=-x;
    		}
    		#undef tc
    }F;
    inline double sqr(const double& x) { return x*x; }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	for (F.read(t);t;--t)
    	{
    		RI i; for (F.read(n),_x=_y=f1=f2=ans=0,i=1;i<=n;++i)
    		F.read(a[i]),_x+=i,_y+=a[i]; _x/=n; _y/=n;
    		for (i=1;i<=n;++i) f1+=(i-_x)*(a[i]-_y),f2+=sqr(i-_x);
    		for (k=f1/f2,b=_y-k*_x,i=1;i<=n;++i) ans+=sqr(a[i]-(k*i+b));
    		printf("%.15lf\n",ans);
    	}
    	return 0;
    }
    

    D. 「NOIP2022模拟赛一 By QY D」化学题

    Pro

    • 有一个数初值为\(1\),每秒钟它就会等概率随机变为原来的\(1,2,3,\cdots,n\)
    • 求该数第一次大于\(m\)的期望时间
    • \(2\le n\le 9\times 10^8,m\le 10^9\)

    Sol

    暴力DP人人都会,就是要注意不变的情况,单独提出来后把转移方程移项后即可:

    \[f_i=(\sum_{j|i\and \frac{i}{j}\in[1,n]} \frac{f_j}{n}) +1\\ \Leftrightarrow f_i=(\sum_{j|i\and \frac{i}{j}\in(2,n]} \frac{f_j}{n}) +\frac{f_i}{n}+1\\ \Leftrightarrow f_i=(\sum_{j|i\and \frac{i}{j}\in(2,n]} \frac{f_j}{n-1}) +\frac{n}{n-1} \]

    考虑从\(j|i\)入手,用整除分块后状态变成了\(\sqrt m\)级别,同时在转移的时候也可以再套一个整除分块来把相同的值一起转移

    总体复杂度\(O(m^{\frac{3}{4}})\)(和杜教筛的复杂度类似,两个除法分块套在一起就是这个复杂度)

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int S=100000,mod=998244353;
    int n,m,sm,st[S<<1],cnt,inv,id1[S],id2[S],f[S<<1];
    inline int quick_pow(int x,int p=mod-2,int mul=1)
    {
    	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
    }
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    #define ID(x) ((x)<=sm?id1[x]:id2[m/(x)])
    int main()
    {
    	RI i,l,r; scanf("%d%d",&n,&m); sm=sqrt(m);
    	for (l=1;l<=m;l=r+1) r=m/(m/l),st[ID(m/l)=++cnt]=m/l;
    	for (inv=quick_pow(n-1),i=cnt;i;--i)
    	{
    		for (l=2;l<=min(n,st[i]);l=r+1)
    		r=min(n,st[i]/(st[i]/l)),inc(f[i],1LL*(r-l+1)*f[ID(st[i]/l)]%mod);
    		f[i]=1LL*(f[i]+n)*inv%mod;
    	}
    	return printf("%d",f[ID(m)]),0;
    }
    

    Postscript

    咕噜咕噜咕噜……

  • 相关阅读:
    mysql
    Spring MVC
    springSecurity
    导出Excel报表
    Redis集群搭建
    Oracle 分析数据库表行长度的统计信息 使用聚簇的步骤
    Dbms.job 学习
    oracel 学习系列
    Oracle 工具类 Sql 分析索引的 碎片率
    oracl
  • 原文地址:https://www.cnblogs.com/cjjsb/p/16591319.html
Copyright © 2020-2023  润新知