2021.03.13【NOIP提高A&B组】模拟 总结

T1

题目大意:从原点开始循环执行命令,问最后的位置

好吧这就是一道幼儿园的周期问题,模拟即可

#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int T,tt,n,xx,yy,lx,ly;
long long fx,fy;
char op[N];
int main() {
	scanf("%s%d",op+1,&T);
	n=strlen(op+1),tt=T/n;
	for(int i=1;i<=n;i++) {
		if(op[i]=='E')++xx;
		else if(op[i]=='W')--xx;
		else if(op[i]=='S')--yy;
		else if(op[i]=='N')++yy;
	}
	for(int i=1;i<=(T%n);i++) {
		if(op[i]=='E')++lx;
		else if(op[i]=='W')--lx;
		else if(op[i]=='S')--ly;
		else if(op[i]=='N')++ly;		
	}
	fx=1LL*xx*tt+lx;
	fy=1LL*yy*tt+ly;
	printf("%lld %lld",fx,fy);
}

T2

初始是 1 ,一次可以加 \(x\) 或加 \(y\) 或加 \(z\) ,问上限为 \(h\) 时最多能的到多少个数

\(40 \ \text{pts}\) :无脑的暴力

\(100 \ \text{pts}\)\(D_i\) 为只用 \(y,z\) 的最小\(c\mod x=i\) ,所以

\(D_0=0\)

\(D_{(i+y)\mod x}=D_i+y\)

\(D_{(i+z)\mod x}=D_i+z\)

这样可以用最短路来转移。答案就是 \(\sum_{i=0}^{x-1}(\lfloor\dfrac{h-D_i}{x}\rfloor+1)*(n\ge D_i)\)

剩下的 \(h-D_i\) 都只用 \(x\) ,就有 \(\lfloor\dfrac{h-D_i}{x}\rfloor\) ,还可以不用 \(x\) ,所以加一

2021.3.15 19:46 发现这是一题经典的同余最短路

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
LL n,res,dis[N];
int x,y,z,cnt,l,r,q[N],vis[N];
int main() {
	scanf("%lld%d%d%d",&n,&x,&y,&z),--n;
	memset(dis,100,sizeof(dis));
	dis[0]=0,q[r=1]=0;
	for(int u,v;l<r;) {
		u=q[++l],vis[u]=0;
		if(dis[u]+y<dis[v=(u+y)%x])dis[v]=dis[u]+y,q[++r]=v;
		if(dis[u]+z<dis[v=(u+z)%x])dis[v]=dis[u]+z,q[++r]=v;
	}
	for(int i=0;i<x;i++)
		if(n>=dis[i])res+=(n-dis[i])/x+1;
	printf("%lld",res);
} 

T3

题意:维护一个序列,操作 1 把 \(L\le\forall i\le R\) 的元素加上 \(F_{i-L+1}\) 其中 \(F\) 表示斐波那契数列

良心的暴力分:\(40\),折腾一个小时线段树的结果

赛后:我*,懒标记可以用 vector !!!为什么不会超时:我也不知道

然后线段树巨大常数会超时,用分块。下传标记、修改、查询都是 \(O(\sqrt n)\) ,总 \(O(m\sqrt n)\)

#include<bits/stdc++.h>
#define RG register
using namespace std;
typedef long long LL;
const LL P=1000000009;
const int N=100005,SQ=1005; 
inline int Rd() {
	RG int x=0;
	char C=getchar();
	for(;C<'0'||C>'9';C=getchar()) ;
	for(;C>'/'&&C<':';C=getchar()) x=(x<<1)+(x<<3)+(C^48);
	return x;
}
LL x[N],sm[SQ],ans,f[N]={0,1,1},s[N]={0,1,2};
vector<int>tg[SQ];
int n,T,pos[N],L[SQ],R[SQ],bl; 
inline void down(RG int p) {
	if(tg[p].empty())return;
	for(RG int i=0;i<tg[p].size();i++)
		for(RG int j=L[p];j<=R[p];j++)
			(x[j]+=f[j-tg[p][i]+1])%=P;
	tg[p].clear();
}
inline void mdy(RG int l,RG int r) {
	RG int p=pos[l],q=pos[r];
	if(p==q) {
		for(RG int i=l;i<=r;i++)
			(x[i]+=f[i-l+1])%=P,
			(sm[p]+=f[i-l+1])%=P;
		return;
	}
	for(RG int i=l;i<=R[p];i++)
		(x[i]+=f[i-l+1])%=P,
		(sm[p]+=f[i-l+1])%=P;
	for(RG int i=p+1;i<=q-1;i++)
		(sm[i]+=(s[R[i]-l+1]-s[L[i]-l]+P)%P)%=P,
		tg[i].push_back(l);
	for(RG int i=L[q];i<=r;i++)
		(x[i]+=f[i-l+1])%=P,
		(sm[q]+=f[i-l+1])%=P;
}
inline void ask(RG int l,RG int r) {
	RG int p=pos[l],q=pos[r];
	if(p==q) {
		down(p);
		for(RG int i=l;i<=r;i++)(ans+=x[i])%=P;
		return;
	}
	down(p),down(q);
	for(RG int i=l;i<=R[p];i++)(ans+=x[i])%=P;
	for(RG int i=p+1;i<=q-1;i++)(ans+=sm[i])%=P;
	for(RG int i=L[q];i<=r;i++)(ans+=x[i])%=P;
}
int main() {
	n=Rd(),T=Rd();
	for(RG int i=3;i<=n;i++)
		f[i]=(f[i-1]+f[i-2])%P,
		s[i]=(s[i-1]+f[i])%P;
	for(RG int i=1;i<=n;i++)x[i]=1LL*Rd();
	bl=sqrt(1.0*n);
	for(RG int i=1;i<=bl;i++)L[i]=R[i-1]+1,R[i]=i*bl;
	if(R[bl]<n)++bl,L[bl]=R[bl-1]+1,R[bl]=n;
	for(RG int i=1;i<=bl;i++)
		for(RG int j=L[i];j<=R[i];j++)
			pos[j]=i,(sm[i]+=x[j])%=P;
	for(RG int opt,l,r;T--;) {
		opt=Rd(),l=Rd(),r=Rd();
		if(opt==1)mdy(l,r);
		else ans=0,ask(l,r),printf("%lld\n",ans);
	}
}

方法 2

维护一个贡献数组 \(d\) ,可以 \(O(1)\) 修改:

\(d_l\leftarrow d_l+1\)

\(d_{r+1}\leftarrow d_{r+1}-f_{r-l+2}\)

\(d_{r+2}\leftarrow d_{r+2}-f_{r-l+1}\)

重算:\(O(n)\) 计算 \(d\) ,即对原数组的贡献 \(d_i=d_{i-1}+d_{i-2}\)

但是这样复杂度依然不理想,发现瓶颈是重算

运用平衡规划(好高级的样子),每 \(k\) 次进行重算

发现询问时不一定刚刚重算完,所以这一段贡献单独算进答案

容易发现当 \(k=\sqrt m\) 时复杂度最优,为 \(O((n+m)\sqrt m)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P=1000000009;
const int N=100005;
int n,T,bl,ls=1,opt[N],l[N],r[N];
LL res,x[N],sm[N],d[N],f[N]={0,1,1},s[N]={0,1,2};
int main() {
	scanf("%d%d",&n,&T);
	for(int i=3;i<=n;i++) {
		f[i]=(f[i-1]+f[i-2])%P;
		s[i]=(s[i-1]+f[i])%P;
	}
	for(int i=1;i<=n;i++) {
		scanf("%d",&x[i]);
		sm[i]=(sm[i-1]+x[i])%P;
	}
	bl=sqrt(1.0*T);
	for(int i=1;i<=T;i++) {
		scanf("%d%d%d",&opt[i],&l[i],&r[i]);
		if(opt[i]==1) {
			++d[l[i]];
			d[r[i]+1]-=f[r[i]-l[i]+2];
			d[r[i]+2]-=f[r[i]-l[i]+1];
		} else {
			res=(sm[r[i]]-sm[l[i]-1]+P)%P;
			for(int j=ls;j<=i;j++)
				if(opt[j]==1 && r[j]>=l[i] && l[j]<=r[i])
					res=(res+s[min(r[i],r[j])-l[j]+1]-s[max(l[i],l[j])-l[j]]+P)%P;
			printf("%lld\n",res);
		}
		if(i%bl==0) {
			sm[1]=(x[1]+=d[1]),ls=i+1;
			for(int j=2;j<=n;j++) {
				(d[j]+=d[j-1]+d[j-2]+P)%=P;
				(x[j]+=d[j]+P)%=P;
				sm[j]=(sm[j-1]+x[j])%P;
			}
			memset(d,0,sizeof(d));
		}
	}
} 

T4

赛时:怎么又是期望 dp ,放弃了

赛后:三行代码认真的吗?

其实就是把相邻两个数的较大值取倒数,然后求和

#include<bits/stdc++.h>
using namespace std;
const int N=10000005;
int n,A,B,C,x[N];
double s;
int main() {
	scanf("%d%d%d%d%d",&n,&A,&B,&C,x+1);
	for(int i=2;i<=n;i++)
		x[i]=(1LL*x[i-1]*A+1LL*B)%100000001;
	for(int i=1;i<=n;i++)x[i]=x[i]%C+1;
	for(int i=1;i<=n;i++)s+=1.0/max(x[i],x[i%n+1]);
	printf("%.3lf",s);
} 

总结

T2:了解了同余最短路,希望下次知道

T3:原来懒标记可以这样+奇妙的差分+平衡规划

T4:不要以为期望 dp 很难

原文地址:https://www.cnblogs.com/KonjakLAF/p/14539726.html