• NOIP2022模拟赛一 By yzxoi 8.15


    Preface

    和保安拉扯了半天才进EZ,然后就被招呼着打模拟赛了

    说实话OI赛制以后可能再难遇到了所以还是有点怀念的说

    结果T3自信写完过了大样例无比自信摸鱼去了,然后炸到40pts了……

    本来以为HHHOJ上的号都废了结果还能拿出来给别人涨Rating,真是太化蜡了

    由于外面上不了HHHOJ所以我就简化一下题面放上去了


    A. 「NOIP2022模拟赛一 By yzxoi A」质数检验

    Pro

    • \(f(x)\)表示大于\(x\)的第一个质数
    • \(t\)组询问,给定\(x\),判断\(\lfloor\frac{f(x)+f(f(x))}{2}\rfloor\)是否为质数
    • \(t\le 10^5,x\le 10^{18}\)

    Sol

    一眼猜测当且仅当\(x=1\)\(g(x)=2\)YES,否则为NO

    证明:若\(x>1\),显然\(\frac{f(x)+f(f(x))}{2}\)为整数,且\(f(x)<\frac{f(x)+f(f(x))}2< f(f(x))\)

    假设\(\frac{f(x)+f(f(x))}{2}\)为质数,则\(f(f(x))\le \frac{f(x)+f(f(x))}2\),矛盾,故\(g(x)\)为合数

    #include<cstdio>
    #define RI register int
    #define CI const int&
    using namespace std;
    int t; long long x;
    int main()
    {
    	for (scanf("%d",&t);t;--t)
    	scanf("%lld",&x),puts(x==1?"YES":"NO");
    	return 0;
    }
    

    B. 「NOIP2022模拟赛一 By yzxoi B」数据结构

    Pro

    • 有长度为\(n\)的序列\(a\),操作系数\(E\),进行\(m\)个操作:
      • 1 l r x:对\([l,r]\)内所有满足\(a_i\le E\)的数加上\(x\)
      • 2 l r:查询\(\max_\limits{l\le i\le r} a_i[a_i\le E]\)
    • \(n,m\le 10^6,0\le x,a_i,E\le 10^6\)

    Sol

    不难发现每个数只会被删除一次,删除之后就不再参与统计了

    因此我们可以直接暴力把超过\(E\)的数直接变成\(-\infty\),由于每次修改最多访问\(O(\log n)\)个节点,因此复杂度还是对的

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    #define int long long
    using namespace std;
    const int N=1000005;
    int n,m,lim,a[N],opt,l,r,x;
    class Segment_Tree
    {
    	private:
    		struct segment
    		{
    			int mx,tag;
    		}node[N<<2];
    		#define M(x) node[x].mx
    		#define T(x) node[x].tag
    		#define TN CI now=1,CI l=1,CI r=n
    		#define LS now<<1,l,mid
    		#define RS now<<1|1,mid+1,r
    		inline void pushup(CI now)
    		{
    			M(now)=max(M(now<<1),M(now<<1|1));
    		}
    		inline void remove(CI now,CI l,CI r)
    		{
    			if (l==r) return (void)(M(now)=-1e18); int mid=l+r>>1;
    			if (T(now)) M(now<<1)+=T(now),M(now<<1|1)+=T(now),T(now<<1)+=T(now),T(now<<1|1)+=T(now),T(now)=0;
    			if (M(now<<1)>lim) remove(LS); if (M(now<<1|1)>lim) remove(RS); pushup(now);
    		}
    		inline void apply(CI now,CI l,CI r,CI x)
    		{
    			T(now)+=x; if ((M(now)+=x)<=lim) return; remove(now,l,r);
    		}
    		inline void pushdown(CI now,CI l,CI r)
    		{
    			int mid=l+r>>1; if (T(now)) apply(LS,T(now)),apply(RS,T(now)),T(now)=0;
    		}
    	public:
    		inline void build(TN)
    		{
    			if (l==r) return (void)(M(now)=a[l]<=lim?a[l]:-1e18);
    			int mid=l+r>>1; build(LS); build(RS); pushup(now);
    		}
    		inline void modify(CI beg,CI end,CI x,TN)
    		{
    			if (beg<=l&&r<=end) return apply(now,l,r,x); int mid=l+r>>1; pushdown(now,l,r);
    			if (beg<=mid) modify(beg,end,x,LS); if (end>mid) modify(beg,end,x,RS); pushup(now);
    		}
    		inline int query(CI beg,CI end,TN)
    		{
    			if (beg<=l&&r<=end) return M(now); int mid=l+r>>1,ret=-1e18; pushdown(now,l,r);
    			if (beg<=mid) ret=max(ret,query(beg,end,LS)); if (end>mid) ret=max(ret,query(beg,end,RS)); return ret;
    		}
    		#undef M
    		#undef T
    		#undef TN
    		#undef LS
    		#undef RS
    }T;
    signed main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i; for (scanf("%lld%lld%lld",&n,&m,&lim),i=1;i<=n;++i)
    	scanf("%lld",&a[i]); for (T.build(),i=1;i<=m;++i)
    	{
    		if (scanf("%lld%lld%lld",&opt,&l,&r),opt==2) printf("%lld\n",max(0LL,T.query(l,r)));
    		else scanf("%lld",&x),T.modify(l,r,x);
    	}
    	return 0;
    }
    

    C. 「NOIP2022模拟赛一 By yzxoi C」括号树

    Pro

    • 给一个\(n\)个节点的树,每个点的点权为(),问所有合法括号序列路径中的最大嵌套数
    • 定义合法括号序列路径\(S\)的最大嵌套数为\(\max_\limits{1\le i\le |S|} \sum_{j=1}^{i} (s_j='('?1:-1)\)
    • \(n\le 40000\)

    Sol

    妈的调了半天发现是一个以前犯过的nt错误,和我的码风有很大的关系,而且极其隐蔽

    大致地,就是在写点分治的过程中,把重心rt开成了全局变量,然后在点分治中写的是:

    inline void solve(const int& now)
    

    在内部调用递归solve(rt)之后直接爆炸……

    然后导致根本没有把所有的点都做一遍,因此惨烈地G了

    回到题目,这道题很经典,因为很早之前做过树上括号序列计数的问题,而且这种静态树上路径统计,肯定是点分治没跑了

    考虑如何在分治中心统计答案,容易想到我们可以先保证子树内部分合法,然后遗留下若干个括号和另外的子树匹配

    显然可以开一个桶来表示之前的子树中在遗留\(x\)个括号的情况下,前缀最大值是多少

    维护的过程显然可以开一个栈来维护,而且要注意维护向下和向下的链时需要遗留的括号是不同的

    最后注意要正反跑两边,因为我的写法是先算往下的再算往上的

    具体实现由些细节,可以看代码,复杂度\(O(n\log n)\)

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #define RI register int
    #define CI const int&
    #define pb push_back
    using namespace std;
    const int N=40005;
    int n,x,a[N],ans,sz[N],bkt[N],mx,rt,stk[N],top,pfx[N],mpfx[N];
    char ch; bool vis[N]; vector <int> v[N];
    #define to v[now][i]
    inline void getsz(CI now=1,CI fa=0)
    {
    	sz[now]=1; for (RI i=0;i<v[now].size();++i) if (!vis[to]&&to!=fa) getsz(to,now),sz[now]+=sz[to];
    }
    inline void getrt(CI tot,CI now=1,CI fa=0)
    {
    	int mn=tot-sz[now]; for (RI i=0;i<v[now].size();++i) if (!vis[to]&&to!=fa)
    	mn=max(mn,sz[to]),getrt(tot,to,now); if (mn<mx) mx=mn,rt=now;
    }
    inline void DFS1(CI now,CI fa)
    {
    	pfx[now]=pfx[fa]+a[now]; mpfx[now]=max(mpfx[fa],pfx[now]);
    	bool ispop=0; if (stk[top]==1&&a[now]==-1) --top,ispop=1; else stk[++top]=a[now];
    	if (~bkt[top]&&(!top||stk[top]==-1)) ans=max(ans,max(bkt[top],mpfx[now])+top);
    	for (RI i=0;i<v[now].size();++i) if (!vis[to]&&to!=fa) DFS1(to,now);
    	if (ispop) stk[++top]=1; else --top;
    }
    inline void DFS2(CI now,CI fa)
    {
    	pfx[now]=pfx[fa]+a[now]; mpfx[now]=min(mpfx[fa],pfx[now]);
    	bool ispop=0; if (stk[top]==-1&&a[now]==1) --top,ispop=1; else stk[++top]=a[now];
    	if (!top||stk[top]==1) bkt[top]=max(bkt[top],-mpfx[now]);
    	for (RI i=0;i<v[now].size();++i) if (!vis[to]&&to!=fa) DFS2(to,now);
    	if (ispop) stk[++top]=-1; else --top;
    }
    inline void solve(int now)
    {
    	RI i; for (i=vis[now]=1,getsz(now),bkt[0]=0;i<=sz[now];++i) bkt[i]=-1;
    	for (i=0;i<v[now].size();++i) if (!vis[to])
    	{
    		top=0; stk[++top]=mpfx[now]=pfx[now]=a[now]; DFS1(to,now);
    		top=0; mpfx[now]=pfx[now]=0; DFS2(to,now); ans=max(ans,bkt[0]);
    	}
    	for (i=1,bkt[0]=0;i<=sz[now];++i) bkt[i]=-1;
    	for (i=v[now].size()-1;~i;--i) if (!vis[to])
    	{
    		top=0; stk[++top]=mpfx[now]=pfx[now]=a[now]; DFS1(to,now);
    		top=0; mpfx[now]=pfx[now]=0; DFS2(to,now); ans=max(ans,bkt[0]);
    	}
    	for (i=0;i<v[now].size();++i) if (!vis[to])
    	mx=sz[to]+1,getrt(sz[to],to,now),solve(rt);
    }
    #undef to
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i; for (scanf("%d",&n),i=2;i<=n;++i) scanf("%d",&x),v[i].pb(x),v[x].pb(i);
    	for (i=1;i<=n;++i)
    	{
    		ch=getchar(); while (ch!='('&&ch!=')') ch=getchar();
    		a[i]=ch=='('?1:-1;
    	}
    	return getsz(),mx=n+1,getrt(n),solve(rt),printf("%d",ans),0;
    }
    

    D. 「NOIP2022模拟赛一 By yzxoi D」三元组

    Pro

    • 给定一个集合\(A\),其中的元素为\(a\)
    • 定义一种得分方式为存在无序三元组\((i,j,k)\),满足下列条件:
      • \(i\ne j,j\ne k,i\ne k\)
      • \(i,j,k\in A\)
      • \(i|j,i|k\)
    • 当该三元组满足条件时,你可以将\(k\)\(A\)中删除,并将\(k\)加入初始为空的删除序列\(S\)的末尾,并获得一分
    • 问最终达到最高分数的不同删除序列\(S\)数量
    • \(|A|,a\le 60\)

    Sol

    刚开始题目看错了,以为是把\(i,j,k\)全删了,大呼SB题

    结果写完发现不对劲,然后就没时间了qwq……

    首先转化模型,若\(x|y\)我们就连一条\(x\to y\)的边,这样可以得到一个弱联通DAG

    不难发现对于每个联通块,答案可以单独计算,并且入度为\(0\)的点是不能删除的

    因此我们可以想到从这里入手,把删点转化为加点

    但是如果只有入度为\(0\)的点也是不能加入别的数的,因此我们还需要至少一个其他的数\(y\),使得有边\(x\to y\),才能加入一个点\(z\)(有边\(x\to z\)

    因此我们可以称当存在\(x\to y\)的边时,\(x\)就被激活了,可以用来加入别的点

    因此我们考虑状压入度为\(0\)的点的激活状态,然后就可以DP了

    因为对于每个联通块,入度为\(0\),并且有出边的点一定只能是\(1\sim 30\),并且选了一个点入度为\(0\),它的倍数就不能选

    所以最多形成了\(1\sim 30\)的最长反链,入度为\(0\)的个数一定\(\le 15\),所以可以状压

    \(f_{i,S}\)表示加入了\(i\)个点,被激活的状态为\(S\)的方案数,转移的时候考虑先处理出\(t_S\)表示被激活的状态为\(S\)时能加入的点的个数

    转移的话分不改变激活状态和改变激活状态两种,注意为了不记重状态不变时的系数为\(t_S-i\)

    最后合并不同的联通块时还有一个细节就是两个序列是可以交叉的,因此要乘上一个组合系数

    综上,设\(n=\frac{|A|}{4}\),复杂度为\(O(2^n\times n^2)\)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #define RI int
    #define CI const int&
    #define pb push_back
    using namespace std;
    const int N=65,S=(1<<15)|5,mod=1e9+7;
    int n,a[N],C[N][N],in[N],f[N][S],clk[N],tot,st[N],cnt,c[N],t[S],ans=1,len; bool vis[N];
    inline void inc(int& x,CI y)
    {
    	if ((x+=y)>=mod) x-=mod;
    }
    inline int sum(CI x,CI y)
    {
    	return x+y>=mod?x+y-mod:x+y;
    }
    inline void DFS(CI now,CI tim)
    {
    	if (clk[now]) return; ++tot; clk[now]=tim;
    	for (RI i=1;i<=n;++i) if (a[i]%a[now]==0||a[now]%a[i]==0) DFS(i,tim);
    }
    int main()
    {
    	RI i,j,k,s; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
    	for (C[0][0]=i=1;i<=n;++i) for (C[i][0]=j=1;j<=n;++j)
    	C[i][j]=sum(C[i-1][j],C[i-1][j-1]);
    	for (i=1;i<=n;++i) for (j=1;j<=n;++j) if (a[i]!=a[j]&&a[i]%a[j]==0) ++in[i];
    	for (k=1;k<=n;++k) if (!clk[k])
    	{
    		tot=cnt=0; DFS(k,k); memset(f,0,sizeof(f)); memset(t,0,sizeof(t)); memset(vis,0,sizeof(vis));
    		for (i=1;i<=n;++i) if (clk[i]==k&&!in[i]) st[++cnt]=a[i],vis[i]=1;
    		if (tot-cnt<=1) continue; for (i=1;i<=n;++i)
    		if (clk[i]==k&&!vis[i])
    		for (j=1;j<=cnt;++j) if (a[i]%st[j]==0) c[i]|=(1<<j-1);
    		int m=(1<<cnt)-1; for (i=0;i<=m;++i) for (j=1;j<=n;++j)
    		if (clk[j]==k&&!vis[j]&&((i&c[j])==c[j])) ++t[i];
    		for (i=1;i<=n;++i) if (clk[i]==k&&!vis[i]) ++f[1][c[i]];
    		for (i=1;i<=tot-cnt-1;++i) for (s=0;s<=m;++s) if (f[i][s])
    		{
    			if (t[s]>i) inc(f[i+1][s],1LL*f[i][s]*(t[s]-i)%mod);
    			for (j=1;j<=n;++j) if (clk[j]==k&&!vis[j]&&((s|c[j])!=s)&&(s&c[j]))
    			inc(f[i+1][s|c[j]],f[i][s]);
    		}
    		int ret=0; for (i=0;i<=m;++i) inc(ret,f[tot-cnt][i]);
    		len+=tot-cnt-1; ans=1LL*ans*ret%mod*C[len][tot-cnt-1]%mod;
    	}
    	return printf("%d",ans),0;
    }
    

    Postscript

    现在好菜的说,希望以后别犯nt错误了……

  • 相关阅读:
    http请求的GET和POST请求:查询和新增(server.php)
    http请求的GET和POST请求:查询和新增(ajax)
    shell中$(( )) 与 $( ) 还有${ }的区别
    linux查看操作系统版本信息
    Linux useradd 与 adduser的区别, /sbin/nologin 与 /bin/bash
    红帽触摸屏屏幕180旋转
    Sed命令n,N,d,D,p,P,h,H,g,G,x解析3
    sed命令
    sed高级用法:模式空间(pattern space)和保持空间(hold space)
    sed的模式空间和保持空间
  • 原文地址:https://www.cnblogs.com/cjjsb/p/16588727.html
Copyright © 2020-2023  润新知