2021.06.12【NOIP提高B组】模拟 总结

T1

题目大意:有 \(n\) 个点,到点 \(i\) 可以获得 \(A_i\) ,同时消耗 \(B_i\)

若当前价值小于 \(B_i\) 则不能到,问从 \(P\) 开始,任一点结束后的最大值。

最大值同时问最多访问点数。开始不用消耗

其实就是把所有 \(A_i\ge B_i\) 的点存下来,然后贪心

考试时脑抽忘记有消耗了。

T2

题意:一个平面上有两个圆心和 \(n\) 个点,每次给你两个圆的半径

问被任意一个圆覆盖的点的个数

考试时拼命想二分,然后憋一小时写了个 0 分

正解离线,知道后直接 WTF ,

\(d_1\) 为到点 \(A\) 的距离,\(d_2\) 为到点 \(B\) 的距离

询问按照 \(r_1\) 排序,点按照 \(d_1\) 排序

由于 \(r_1\) 递增,所以 \(r_{i+1}\)\(r_i\) 有公共的答案,

可以均摊 \(O(1)\) 的时间求出点 \(A\) 包含的点,并把它们在树状数组中标记

于是 \(B\) 包含的不重复的点就是那些距离在 \(r_2\) 内且没被标记的点

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005,M=200000;
int n,m,tmp,ans[N],t[N],dy[N],l,r,mid,res;
LL ax,ay,bx,by,r1,r2,d2[N],d1[N];
struct qry { LL r1,r2; int id; }q[N];
inline LL sqr(LL x) { return x*x; }
inline bool cmp(qry a,qry b) { return a.r1<b.r1; }
inline void add(int p,int v) { for(;p<=M;p+=p&-p)t[p]+=v; }
inline int ask(int p) { register int s=0; for(;p;p-=p&-p)s+=t[p]; return s; }
void sor(int l,int r) {
	register int i=l,j=r;
	register LL mid=d1[(l+r)>>1];
	while(i<=j) {
		while(d1[i]<mid)i++;
		while(d1[j]>mid)j--;
		if(i<=j) {
			swap(d1[i],d1[j]);
			swap(d2[i],d2[j]);
			i++,j--;
		}
	}
	if(i<r)sor(i,r);
	if(j>l)sor(l,j);
}
int main() {
	scanf("%d%d",&n,&m);
	scanf("%lld%lld%lld%lld",&ax,&ay,&bx,&by);
	for(int i=1,x,y;i<=n;i++) {
		scanf("%d%d",&x,&y);
		d1[i]=ceil(sqrt(sqr(x-ax)+sqr(y-ay)));
		d2[i]=ceil(sqrt(sqr(x-bx)+sqr(y-by)));
		add(d2[i],1);
	}
	sor(1,n);
	for(int i=1;i<=m;i++) {
		scanf("%lld%lld",&q[i].r1,&q[i].r2);
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	for(int i=1,j=1;i<=m;i++) {
		for(;d1[j]<=q[i].r1;j++)
			add(d2[j],-1);
		ans[q[i].id]=j-1+ask(q[i].r2);
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}

T3

题目大意:求 \(\sum_{i=m}^n i^k\)\(p\) 意义下的值

对于 \(n-m\le 5000\) 的,直接暴力

然后剩下来的都是 \(k\le 2000\) 的数据

其实是恐怖如斯的自然数幂和,用第二类斯特林数

\(S_n^m=S_{n-1}^{m-1}+kS_{n-1}^m\)

定理:\(n^k=\sum_{i=0}^k S_k^i\times i!\times C_n^i\)

于是有

\[\begin{aligned} tmp &= \sum_{i=1}^n i^k \\ &= \sum_{a=1}^n\sum_{i=0}^k S_k^i\times i!\times C_a^i \\ &= \sum_{i=0}^k S_k^i\times i! \sum_{a=1}^n C_a^i \\ &= \sum_{i=0}^k S_k^i\times i! \times C_{n+1}^{i+1} \\ &= \sum_{i=0}^k S_k^i\times i! \times \dfrac{(n+1)!}{(i+1)!(n-i)!} \\ &= \sum_{i=0}^k S_k^i \times \dfrac{(n+1)!}{(i+1)(n-i)!} \\ &= \sum_{i=0}^k S_k^i \times \dfrac{\Pi_{j=n-i+1}^{n+1}}{i+1} \end{aligned} \]

其中 \(\sum_{a=1}^n C_a^i=C_{n+1}^{i+1}\) 可以由 \(C_n^m=C_{n-1}^{m-1}+C_{n-1}^m\) 展开

由于在区间 \([n-i+1,n+1]\) 中有且只有一个 \(i+1\) 的倍数,循环时找出即可

复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
typedef long double LD;
typedef long long LL;
typedef unsigned long long uLL;
LL K,n,m,P,ans,tmp,s[2005][2005];
inline LL Mul(uLL x,uLL y) {
	return (x*y-(LL)((LD)x/P*y)*P+P)%P;
}
inline LL Pow(LL x,LL y) {
	register LL res=1;
	for(;y;y>>=1,x=Mul(x,x))
	if(y&1)res=Mul(res,x);
	return res;
}
inline LL Ans(LL n) {
	ans=0;
	for(int i=1;i<=K;i++) {
		tmp=s[K][i];
		for(LL j=n-i+1;j<=n+1;j++) {
			if(j%(i+1))tmp=Mul(tmp,j);
			else tmp=Mul(tmp,j/(i+1));
		}
		ans=(ans+tmp)%P;
	}
	return ans;
}
int main() {
	scanf("%lld%lld%lld%lld",&K,&n,&m,&P);
	if(n-m<=5000) {
		for(;m<=n;m++)(ans+=Pow(m,K))%=P;
		return printf("%lld",ans),0;
	}
	s[0][0]=1;
	for(int i=1;i<=K;i++)
		for(int j=1;j<=K;j++)
			s[i][j]=(s[i-1][j-1]+j*s[i-1][j]%P)%P;
	printf("%lld",(Ans(n)-Ans(m-1)+P)%P);
}

T4

题意:给你 \(n\) 个点,每个点会一直想着它的目标点移动,设运动时间无限长

一个点如果跟上它的目标点,就会跟着目标点一起移动

问一组不知道往哪移动的点。一个点不知道的如何移动,当且仅当它的移动方向只决定于它自己的移动方向

前方高能

题目很复杂,其实就是让你找环!

为什么?

因为这是一个 \(n\) 个点 \(n\) 条边的图,至少有一个环

而这个环上的点在移动时距离不断减少,最终都不知道往哪移动

所以,坐标根本没用!

随便从一点出发,找到环即可

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,st,to[N],vis[N],ans[N];
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&to[i]);
	st=1;
	while(!vis[st]) {
		vis[st]=1;
		st=to[st];
	}
	for(int i=to[st];i^st;i=to[i])
		ans[++ans[0]]=i;
	ans[++ans[0]]=st;
	printf("%d\n",ans[0]);
	for(int i=1;i<=ans[0];i++)
		printf("%d ",ans[i]);
}

总结

T1:不要忘记条件

T2:在线难就离线

T3:自然数幂和

T4:阅读理解

posted @ 2021-06-16 20:04  小蒟蒻laf  阅读(64)  评论(0编辑  收藏  举报