A - 补给站
题目大意:
给两个补给站坐标和 n n n 个点的坐标。 q q q 次询问,每次询问回答在以两个补给站坐标为圆心,半径为 r 1 , r 2 r_1,r_2 r1,r2 的两个圆形范围内,共多少个点。
思路:
首先明确如何知道点与补给站的距离。(不是欧几里得距离!!)
这里要用到勾股定理, x , y x,y x,y 坐标的差就是两点之间一个直角三角形的两条直角边。剩下不用我多说。
赛时,第一时间是想求两个前缀和,分别对应两个补给站,结果发现重复部分太难处理。
换一种思路。
考虑先以与一个补给站的距离为标准,先找到与这个补给站符合条件的点,再找另一个,避免重复。
观察到,补给站的位置不会变化,所以可以同样可以将询问以一个补给站的半径排序。
这样,某一个补给站的半径单调递增,设某一个当前询问半径为 a a a ,前一个询问半径为 b b b ,那么,以 a a a 为半径时合法的点,在半径为 b b b 时,必定合法。
接下来,就是如何去重,找另一个点。
我们先钦定按第一个点的标准排序。
将每一个点与两点的距离以与第一个点的距离为第一标准排序。
可以很容易想到,一个个去找是不可行的。
尝试树状数组,以前缀和优化。
每次将已经与第一个补给站合法配对的点的贡献 1 1 1 从树状数组中删除。
设当前与第一个补给站匹配的指针为 k k k 。
那么,每次询问答案就是 k+query(a[k].y) 。
到这就做完了。
C o d e : \mathcal Code: Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){int x = 0; bool f = 1; char ch = getchar();while (ch < '0' || ch > '9') { if (ch == '-') f = 0; ch = getchar(); }while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();return (f ? x : (-x));}
const int N=1e6+5;
int n,m;
int ax,ay,bx,by;
struct node{
int num1,num2,id;
}a[N];
struct node1{
int x,y,id;
}ask[N];
bool cmp(node a,node b){
return a.num1<b.num1;
}
bool cmp1(node1 a,node1 b){
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
int lowbit(int x){return (x&(-x));}
int tree[N];
void add(int x,int y){
for(int i=x;i<=300005;i+=lowbit(i))tree[i]+=y;
}
int query(int x){
int ans=0;
for(int i=x;i>0;i-=lowbit(i))ans+=tree[i];
return ans;
}
int ans[N];
signed main(){
// freopen("in.in","r",stdin);
// freopen("myans.out","w",stdout);
scanf("%lld%lld",&n,&m);
scanf("%lld%lld%lld%lld",&ax,&ay,&bx,&by);
for(int i=1;i<=n;i++){
int q,p;
scanf("%lld%lld",&q,&p);
a[i]={(long long)ceil(sqrt(abs(ax-q)*abs(ax-q)+abs(ay-p)*abs(ay-p))),(long long)ceil(sqrt(abs(bx-q)*abs(bx-q)+abs(by-p)*abs(by-p))),i};
//cout<<a[i].num2<<endl;
add(a[i].num2+1,1);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=m;i++)scanf("%lld%lld",&ask[i].x,&ask[i].y),ask[i].id=i;
sort(ask+1,ask+1+m,cmp1);
//for(int i=1;i<=m;i++)cout<<ask[i].x<<" "<<ask[i].y<<endl;
int k=1;
for(int i=1;i<=m;i++){
while(k<=n&&a[k].num1<=ask[i].x){
//cout<<k<<endl;
add(a[k].num2+1,-1);
k++;
}
ans[ask[i].id]=k-1+query(ask[i].y+1);
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
//fclose(stdout);
return 0;
}
B - WYF的盒子
(数据是自己随机生成的,有点水) 可以私信我,提供你的hack数据。
题目大意:
求 ∑ i = m n i k m o d p \Large\sum^{n}_{i=m}i^k\bmod p ∑i=mnikmodp。
没了,就这么简单!
思路:
关键在于,这道题,你暴力绝对过不去!
就是恶心你。
然后,就必须要用第二类斯特林数这种更恶心的东西。
首先钦定 m = 1 m=1 m=1 。
显然:
a
n
s
=
∑
i
=
1
n
i
k
(
1
)
=
∑
i
=
1
n
∑
j
=
0
k
{
k
j
}
i
j
‾
(
2
)
=
∑
i
=
1
n
∑
j
=
0
k
{
k
j
}
∏
d
=
i
−
j
+
1
i
d
(
3
)
=
∑
i
=
1
n
∑
j
=
0
k
{
k
j
}
×
j
!
×
(
i
j
)
(
4
)
=
∑
j
=
0
k
{
k
j
}
j
!
∑
i
=
j
n
(
i
j
)
(
5
)
=
∑
j
=
0
k
{
k
j
}
j
!
(
n
+
1
j
+
1
)
(
6
)
=
∑
j
=
0
k
{
k
j
}
j
!
(
n
+
1
)
!
(
j
+
1
)
!
(
n
−
j
)
!
(
7
)
=
∑
j
=
0
k
{
k
j
}
(
j
+
1
)
−
1
∏
p
=
n
+
1
−
j
n
+
1
p
(
8
)
\begin{aligned} ans&=\sum^{n}_{i=1}i^k~~~~~~~~~~~~~~~~&(1)\\ &=\sum^{n}_{i=1}\sum^{k}_{j=0}{k\brace j}i^{\underline{j}}&(2)\\ &=\sum^{n}_{i=1}\sum^{k}_{j=0}{k\brace j}\prod^{i}_{d=i-j+1}d&(3)\\ &=\sum^{n}_{i=1}\sum^{k}_{j=0}{k\brace j}\times j!\times\binom{i}{j}&(4)\\ &=\sum^{k}_{j=0}{k\brace j}j!\sum^{n}_{i=j}\binom{i}{j}&(5)\\ &=\sum^{k}_{j=0}{k\brace j}j!\binom{n+1}{j+1}&(6)\\ &=\sum^{k}_{j=0}{k\brace j}j!\frac{(n+1)!}{(j+1)!(n-j)!}&(7)\\ &=\sum_{j=0}^{k}{k\brace j}(j+1)^{-1}\prod^{n+1}_{p=n+1-j}p&(8) \end{aligned}
ans=i=1∑nik =i=1∑nj=0∑k{jk}ij=i=1∑nj=0∑k{jk}d=i−j+1∏id=i=1∑nj=0∑k{jk}×j!×(ji)=j=0∑k{jk}j!i=j∑n(ji)=j=0∑k{jk}j!(j+1n+1)=j=0∑k{jk}j!(j+1)!(n−j)!(n+1)!=j=0∑k{jk}(j+1)−1p=n+1−j∏n+1p(1)(2)(3)(4)(5)(6)(7)(8)
( 2 ) (2) (2) 运用公式:
x n = ∑ k { n k } x k ‾ x^n=\sum_{k}{n\brace k}x^{\underline k} xn=k∑{kn}xk
( 3 ) (3) (3) 转化为连乘。( 4 ) (4) (4) 将连乘转化为组合数。将 j ! × ( i j ) j!\times\binom{i}{j} j!×(ji) 拆开可知。
( 6 ) (6) (6) 因 ( n m ) = ( n − 1 m − 1 ) + ( n − 1 m ) \binom{n}{m}=\binom{n-1}{m-1}+\binom{n-1}{m} (mn)=(m−1n−1)+(mn−1) ,所以 ∑ i = j n ( i j ) \sum^{n}_{i=j}\binom{i}{j} ∑i=jn(ji) 即可发现该结论。
( 8 ) (8) (8) 因为 ∏ p = n + 1 − j n + 1 p \prod^{n+1}_{p=n+1-j}p ∏p=n+1−jn+1p 共是连续 j + 1 j+1 j+1 个数的乘积,所以 ∏ p = n + 1 − j n + 1 p \prod^{n+1}_{p=n+1-j}p ∏p=n+1−jn+1p 必定能整除 j + 1 j+1 j+1 。
公式推理完毕。
现在考虑怎么求 m ≠ 0 m\ne 0 m=0 的情况。
很简单,上述在 m = 0 m=0 m=0 的情况下,相当于求得了 ∑ i = 1 n i k \sum^{n}_{i=1}i^k ∑i=1nik ,那么我们把 n n n 换成 m m m 再做一遍,两次得到的答案相减,就是正确答案。
由于本题的特殊性质,我们可以用上述方法求得 k ≤ 2000 k \le 2000 k≤2000 的情况,若 k > 2000 k>2000 k>2000 就用快速幂解决,因为在 k > 2000 k>2000 k>2000 时,有特殊性质规定 n − m ≤ 5000 n-m\le 5000 n−m≤5000 。
C o d e : \mathcal Code: Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){int x = 0; bool f = 1; char ch = getchar();while (ch < '0' || ch > '9') { if (ch == '-') f = 0; ch = getchar(); }while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();return (f ? x : (-x));}
const int N=1e6+5;
__int128 num[2001][2001];
__int128 Pow(__int128 x,__int128 y,__int128 p){
__int128 res=1;
while(y){
if(y&1)res=res*x%p;
x=x*x%p;
y>>=1;
}
return res;
}
void work1(int n,int m,int k,int p){
__int128 ans=0;
for(int i=m;i<=n;i++)ans=(ans+Pow(i,k,p)%p)%p;
printf("%lld\n",(long long)ans);
}
void init(int k,int p){
num[0][0]=1;
for(int i=1;i<=k;i++)for(int j=1;j<=k;j++)num[i][j]=(num[i-1][j-1]+num[i-1][j]*j%p)%p;
}
__int128 work2(int n,int k,int p){
__int128 res=0;
for(int i=0;i<=k;i++){
__int128 ksum1=1;
for(int j=n+1-i;j<=n+1;j++)ksum1=(ksum1*(j%(i+1)==0?j/(i+1):j))%p;
res=(res+num[k][i]*ksum1)%p;
}
return res;
}
signed main(){
int k,n,m,p;
scanf("%lld%lld%lld%lld",&k,&n,&m,&p);
if(k>2000)work1(n,m,k,p);
else{
init(k,p);
printf("%lld\n",(long long)((work2(n,k,p)-work2(m-1,k,p)+p)%p));
}
return 0;
}

156

被折叠的 条评论
为什么被折叠?



