• NOIP2022模拟赛二 By yzxoi 8.17


    Preface

    今天早上被公交车搞了,晚了30min才到……

    最后T1读入\(n\)的时候写%d了,喜提30pts(结果Rank竟然不变233)


    A. 「NOIP2022模拟赛二 By yzxoi A」『Pale』/ feat. 初音ミク

    Pro

    • 有线性递推数列\(F\)满足:

      \[F(n)=3F(n-1)+2F(n-2)(n\ge 2)\\ F(0)=0,F(1)=1 \]

    • \(Q\)次询问,每次询问\(F(n)\mod 998244353\),强制在线

    • \(Q\le 10^7,n\le 10^{18}\)

    Sol

    以前学OI的时候推不来这种东西,现在参加完高考后发现还没平时做的题难……

    首先构造等比数列求解,先待定个系数\(\alpha.\beta\),有:

    \[f(n)+\beta\cdot f(n-1)=\alpha\cdot (f(n)+\beta\cdot f(n-1))(n\ge 2)\\ \Leftrightarrow f(n)=(\alpha -\beta)\cdot f(n-1)+\alpha\beta\cdot f(n-2)(n\ge 2) \]

    由递推式解得:

    \[\begin{cases}\alpha_1=\frac{3+\sqrt{17}}{2}\\\beta_1=\frac{\sqrt{17}-3}{2}\end{cases},\begin{cases}\alpha_2=\frac{3-\sqrt{17}}{2}\\\beta_2=-\frac{3+\sqrt{17}}{2}\end{cases} \]

    得到:

    \[\begin{cases}f(n)+\beta_1\cdot f(n-1)=\alpha_1^{n-1}\cdot(f(1)+\beta_1\cdot f(0))\\f(n)+\beta_2\cdot f(n-1)=\alpha_2^{n-1}\cdot(f(1)+\beta_2\cdot f(0))\end{cases} \]

    联立消去\(f(n-1)\)得到\(f(n)=\frac{\alpha_2^n\beta_1-\alpha_1^n\beta _2}{\beta_1-\beta_2}=\frac{(\frac{3+\sqrt{17}}{2})^n-(\frac{3-\sqrt{17}}{2})^n}{\sqrt {17}}\)

    然后由于数据范围很大我们需要\(O(1)\)快速幂,直接根号分治一下即可

    #include<cstdio>
    #define RI register int
    #define CI const int&
    #define CL const long long&
    using namespace std;
    const int mod=998244353,S=100000,sqrt_17=473844410;
    int q,a1[S+5],ap1[S],a2[S+5],ap2[S],inv,cur,ans; long long 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;
    }
    inline int QP(int *A,int *B,int p)
    {
    	return 1LL*A[p%S]*B[p/S]%mod;
    }
    inline int sub(CI x,CI y)
    {
    	return x-y<0?x-y+mod:x-y;
    }
    inline int F(CL n)
    {
    	return 1LL*sub(QP(a1,ap1,n%(mod-1)),QP(a2,ap2,n%(mod-1)))*inv%mod;
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i; scanf("%d%lld",&q,&n); inv=quick_pow(sqrt_17); a1[0]=a2[0]=ap1[0]=ap2[0]=1;
    	a1[1]=1LL*(3+sqrt_17)*quick_pow(2)%mod; a2[1]=1LL*(3-sqrt_17+mod)*quick_pow(2)%mod;
    	for (i=2;i<=S;++i) a1[i]=1LL*a1[i-1]*a1[1]%mod,a2[i]=1LL*a2[i-1]*a2[1]%mod;
    	for (i=1;i<S;++i) ap1[i]=1LL*ap1[i-1]*a1[S]%mod,ap2[i]=1LL*ap2[i-1]*a2[S]%mod;
    	for (ans=cur=F(n),i=2;i<=q;++i) n=n^(1LL*cur*cur),cur=F(n),ans^=cur;
    	return printf("%d",ans),0;
    }
    

    B. 「NOIP模拟赛二 By yzxoi B」Oval and Function

    Pro

    • 求不定方程\(x^2+3y^2=N^2\)的整数解的个数
    • 数据组数\(T\le 10\)\(n\le 10^{12}\)

    Sol

    刚开始写了个假算法求助陈指导被点破后发现这题两年前校内模拟赛做过,但我已经完全没有印象了的说

    先讲一个假算法,刚开始考虑到利用平方换元法(也是高考中常用方法233),得到:

    \[\begin{cases}x=k\cdot(m^2-3n^2)\\y=k\cdot(2mn)\\N=k\cdot(m^2+3n^2) \end{cases} \]

    其中\(k,m,n\)均为整数,只要枚举\(k|N\)然后再枚举\(n\)即可

    但是这样并不能表示所有的情况,因为\(y\)显然只能是偶数

    考虑正解,首先由于\(x=\pm n,y=0\)是显然成立的两组通解,而\(x=0\)时显然无解

    并且若\((x,y),x,y\in N^*\)为一组解,那么\((x,-y),(-x,y),(-x,-y)\)也都是解

    因此我们可以强制\(x,y\in N^*\),最后把答案乘\(4\)再加\(2\)即可

    考虑令\(a=N-x\),则:

    \[(N-a)^2+3y^2=N^2\Leftrightarrow -2Na+a^2+3y^2=0\Leftrightarrow 3y^2={a\cdot(2N-a)} \]

    \(d=\gcd(a,2N-a)=\gcd(a,2N)\),在上面的式子两边同时除以\(d^2\)得到

    \[3(\frac{y}{d})^2=\frac{a}{d}\cdot \frac{2N-a}{d},(\gcd(\frac{a}{d},\frac{2N-a}{d})=1) \]

    互质的两数相乘是一个平方数的\(3\)倍,则这两个数必然一个是平方数,一个是某平方数的\(3\)

    因此设\(\frac{a}{d}=3i^2,\frac{2N-a}{d}=j^2(i,j\in N^*,\gcd(3i,j)=1)\)(反之亦然),于是:

    \[\frac ad+\frac{2N-a}d=3i^2+j^2\Leftrightarrow \frac{2N}d=3i^2+j^2 \]

    因此直接枚举\(d|2N\),然后枚举\(i\)即可,复杂度上界是\(O(T\cdot n^{\frac{3}{4}})\),显然跑不满

    #include<cstdio>
    #include<cmath>
    #define RI register int
    #define CI const int&
    #define int long long
    using namespace std;
    int t,n;
    inline int gcd(CI n,CI m)
    {
    	return m?gcd(m,n%m):n;
    }
    inline int calc(CI x,int ret=0)
    {
    	for (RI i=1;3LL*i*i<=x;++i)
    	{
    		int y=sqrt(x-3LL*i*i); if (y*y!=x-3LL*i*i) continue;
    		if (gcd(3LL*i,y)==1) ++ret;
    	}
    	return ret;
    }
    signed main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	for (scanf("%lld",&t);t;--t)
    	{
    		int ans=0; scanf("%lld",&n); n<<=1; for (RI i=1;i*i<=n;++i)
    		if (n%i==0) if (ans+=calc(n/i),i*i!=n) ans+=calc(i);
    		printf("%lld\n",(ans<<2LL)+2);
    	}
    	return 0;
    }
    

    C. 「NOIP2022模拟赛二 By yzxoi C」『 だきしめるまで。』/ feat. 可不

    Pro

    • 给定一个\(n\)个点,\(m\)条边的有向图,\(q\)次询问从\(s\)\(t\)经过至少\(k\)条边的最短路
    • 数据组数\(T\le 10,2\le n\le 50,1\le m,k\le 10^4,1\le q\le 10^5\)

    Sol

    首先不难想到一个暴力做法,设\(f_{t,i,j}\)表示从\(i\)恰好经过\(t\)条边到\(j\)的最短路

    但这样做因为\(k\le 10000\)显然是会爆炸的,考虑拆分问题

    而这种拆分的方法一般就两种:倍增根号分治,这道题显然不好倍增,因此考虑根号分治

    \(a_{t,i,j}\)表示从\(i\)至少经过\(t\)条边到\(j\)的最短路,\(pa_{t,i,j}\)表示\(i\)恰好经过\(100t\)条边到\(j\)的最短路

    每次询问\(s\to t\)时考虑枚举一个中间点\(i\),设\(p=k\operatorname{mod} 100,q=\lfloor\frac{k}{100}\rfloor\),则答案就是

    \[\min_\limits{1\le i\le n} (pa_{q,s,i}+a_{p,i,t}) \]

    复杂度\(O(100n^3+qn)\)

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    #define CL const long long&
    using namespace std;
    const int N=55,S=105,INF=1e9;
    int T,n,m,q,x,y,z,c[N][N],d[N][N],a[S][N][N],pa[S][N][N],tmp[N][N];
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	for (scanf("%d",&T);T;--T)
    	{
    		RI i,j,k,t; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) for (j=1;j<=n;++j)
    		c[i][j]=INF,a[0][i][j]=pa[0][i][j]=(i==j?0:INF);
    		for (i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),c[x][y]=min(c[x][y],z);
    		for (t=1;t<=100;++t) for (i=1;i<=n;++i) for (j=1;j<=n;++j) 
    		for (a[t][i][j]=INF,k=1;k<=n;++k) a[t][i][j]=min(a[t][i][j],a[t-1][i][k]+c[k][j]);
    		for (t=1;t<=100;++t) for (i=1;i<=n;++i) for (j=1;j<=n;++j)
    		for (pa[t][i][j]=INF,k=1;k<=n;++k) pa[t][i][j]=min(pa[t][i][j],pa[t-1][i][k]+a[100][k][j]);
    		for (i=1;i<=n;++i) for (j=1;j<=n;++j) d[i][j]=(i==j?0:c[i][j]);
    		for (k=1;k<=n;++k) for (i=1;i<=n;++i) for (j=1;j<=n;++j)
    		d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
    		for (t=0;t<=100;++t)
    		{
    			for (i=1;i<=n;++i) for (j=1;j<=n;++j) tmp[i][j]=INF;
    			for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
    			tmp[i][j]=min(tmp[i][j],pa[t][i][k]+d[k][j]);
    			for (i=1;i<=n;++i) for (j=1;j<=n;++j) pa[t][i][j]=tmp[i][j];
    		}
    		for (scanf("%d",&q);q;--q)
    		{
    			scanf("%d%d%d",&x,&y,&z); int p=z%100,q=z/100,ans=INF;
    			for (i=1;i<=n;++i) ans=min(ans,a[p][x][i]+pa[q][i][y]);
    			printf("%d\n",ans!=INF?ans:-1);
    		}
    	}
    	return 0;
    }
    

    D. 「NOIP2022模拟赛二 By yzxoi D」『くうになる』 / feat. 初音ミク & 可不

    Pro

    • 给定长度为\(n\)的序列\(A\),求:

      \[\sum_{1\leq i < j\leq n}\max\{A_i,A_{i+1},\cdots,A_j\} [(A_i\&A_j)=A_j \lor (A_i\& A_j)=A_i] \]

    • \(n\le 10^5,0\le A_i< 10^{14}\)

    Sol

    首先这种区间最值除了经典的单调栈+分治外,还有一种更加普适的强大科技笛卡尔树

    这样最值的问题解决了,考虑后面那个限制怎么统计,我们不妨设要判断\(x\& y=x\)

    考虑到\(A_i\)一共就\(14\)位,因此直接用一个经典trick(来源:2020年CSPS初赛最后一题),把每个数拆成两段来考虑

    考虑先固定\(x\)的前\(7\)位,将后\(7\)位调大的所有可能都记录下来

    然后固定\(y\)的后\(7\)位,将前\(7\)位条小的所有可能也都记录下来

    这样我们发现这样可能可以统计到一次相同的

    具体计算的时候我们利用笛卡尔树的性质,对于某个点\(x\),它是它子树代表的这个区间里的\(\max\),并且它的左子树就是左边的一段区间,右子树就是右边的一段区间

    考虑到这个答案的统计方法是访问某一边的所有点然后在另一边查询,为了降低复杂度我们可以用DSU on Tree

    具体的,我们每次比较每个节点左子树和右子树的大小,先统计完大的子树中的答案然后保留下来取左小的子树

    由于每个点最多被计算\(\log n\)次,因此复杂度为\(O(2^7\times n\log n)\)

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=10000005;
    int n,a[N],rt; long long ans;
    class Cartesian_tree
    {
    	private:
    		struct CT
    		{
    			int ch[2],sz;
    		}node[N]; int stk[N],top,val,f[1<<15],g[1<<15],h[1<<15];
    		#define lc(x) node[x].ch[0]
    		#define rc(x) node[x].ch[1]
    		#define ch(x,y) node[x].ch[y]
    		#define sz(x) node[x].sz
    		inline void add(CI x,CI y)
    		{
    			int A=x>>7<<7,B=x&127; h[x]+=y; for (RI i=0;i<128;++i)
    			{
    				if ((B&i)==B) f[A|i]+=y; if ((B&i)==i) g[A|i]+=y;
    			}
    		}
    		inline void ask(CI x)
    		{
    			int A=x>>7,B=x&127; ans-=1LL*val*h[x]; for (RI i=0;i<=128;++i)
    			{
    				if ((A&i)==i) ans+=1LL*val*f[(i<<7)|B];
    				if ((A&i)==A) ans+=1LL*val*g[(i<<7)|B];
    			}
    		}
    		inline void modify(CI now,CI v)
    		{
    			add(a[now],v); if (lc(now)) modify(lc(now),v); if (rc(now)) modify(rc(now),v);
    		}
    		inline void query(CI now)
    		{
    			ask(a[now]); if (lc(now)) query(lc(now)); if (rc(now)) query(rc(now));
    		}
    	public:
    		inline void build(void)
    		{
    			for (RI i=1;i<=n;++i)
    			{
    				while (top&&a[i]>a[stk[top]]) --top;
    				if (top) lc(i)=rc(stk[top]),rc(stk[top])=i;
    				else lc(i)=stk[top+1]; stk[++top]=i;
    			}
    			rt=stk[1];
    		}
    		inline void getsz(CI now=rt)
    		{
    			if (sz(now)=1,lc(now)) getsz(lc(now)),sz(now)+=sz(lc(now)); if (rc(now)) getsz(rc(now)),sz(now)+=sz(rc(now));
    		}
    		inline void DSU(CI now=rt)
    		{
    			int d=sz(lc(now))<sz(rc(now));if (ch(now,d^1)) DSU(ch(now,d^1)),modify(ch(now,d^1),-1);
    			if (ch(now,d)) DSU(ch(now,d)); add(a[now],1); val=a[now]; ask(a[now]);
    			if (ch(now,d^1)) query(ch(now,d^1)),modify(ch(now,d^1),1);
    		}
    		#undef lc
    		#undef rc
    		#undef ch
    		#undef sz
    }T;
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),ans-=a[i];
    	return T.build(),T.getsz(),T.DSU(),printf("%lld",ans),0;
    }
    
  • 相关阅读:
    自定义时间工具类
    http和https
    客户端是什么意思
    19.在HTTP 1.0中,状态码401的含义是(?);如果返回“找不到文件”的提示,则可用 header 函数,其语句为(?)写出http常见的状态码和含义,至少5个.[完善题目]
    20.谈谈对mvc的认识。
    18.有一个网页地址, 比如PHP开发资源网主页: http://www.phpres.com/index.html,如何得到它的内容?
    17.如何修改SESSION的生存时间。
    16.语句include和require的区别是什么?为避免多次包含同一文件,可用(?)语句代替它们?
    14.对于大流量的网站,您采用什么样的方法来解决访问量问题?
    15.用PHP写出显示客户端IP与服务器IP的代码,如何防止用户使用代理的情况?[添加更多详情]
  • 原文地址:https://www.cnblogs.com/cjjsb/p/16595855.html
Copyright © 2020-2023  润新知