目录
6609 、Find the answer (划分树计算区间前K大的和)
6608、 Fansblog (米勒罗宾+威尔逊)
题意:
给你一个 内的质数p,求小于 p 的最大质数的阶乘取模p
分析:
对于大素数,可以用米勒罗宾进行素数测试
对于阶乘,由威尔逊定理可得:
对于任意的正质数K,有:()%
所以易得:
所以q!很容易计算出来了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int times=12;
ll mulit(ll a,ll b,ll mod){
ll ret=0;
while(b) {
if(b & 1) ret=(ret+a)%mod;
a=(a+a)%mod;
b >>= 1;
}
return ret;
}
ll quick_mod(ll a,ll b,ll mod) {
ll ret = 1;
while(b) {
if(b & 1) ret = mulit(ret,a,mod);
a = mulit(a,a,mod);
b >>= 1;
}
return ret;
}
bool check(ll a,ll n){
ll x = n - 1;
int t = 0;
while((x & 1) == 0) {
x >>= 1;
t ++;
}
x = quick_mod(a,x,n);
ll y;
for(int i=1;i<=t;i++) {
y = mulit(x,x,n);
if(y == 1 && x != 1 && x != n - 1) return true;
x = y;
}
if(y != 1) return true;
return false;
}
bool Miller_Rabin(ll n) {
if(n == 2) return true;
if(n == 1 || !(n & 1)) return false;
const int arr[12] = {2,3,5,7,11,13,17,19,23,29,31,37};
for(int i = 0; i < times; i++) {
if (arr[i] >= n) break;
if(check(arr[i], n)) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
ll p,q;
scanf("%lld",&p);
for(q=p-2;;q-=2){
if(Miller_Rabin(q)) break;
}
ll ans=1ll;
q++;
while(q<=p-2){
ans=mulit(ans,q,p);
q++;
}
ans=quick_mod(ans,p-2,p)%p;
printf("%lld\n",ans);
}
return 0;
}
6609 、Find the answer (划分树计算区间前K大的和)
题意:
给定一个长度是n的序列w和一个整数m,对于,你能选择一些元素k
将其变为0,使得
,并且要求使得k的值最小。
分析:
可以知道选最小的元素使得和小于m,那么肯定选择前i和元素中最大的,那么很容易想到计算区间前k大的前缀和,并且二分计算即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+2;
typedef long long ll;
int tree[19][maxn];
ll sum[19][maxn];
int toleft[19][maxn];//划分后的结果,前缀和,进入左子树的个数
int a[maxn],sorted[maxn];
void init(int l,int r){
for(int i=l;i<=r;i++) sum[0][i]=sum[0][i-1]+(tree[0][i]=a[i]);
}
/// 向下更新tree和sum数组,同时维护toleft数组的值
void build(int l,int r,int dep){
if(l==r)return;
int mid=(l+r)>>1;
int same=mid-l+1;//表示等于中间值而且被分入左边的个数
for(int i=l;i<=r;i++)
if(tree[dep][i]<sorted[mid])
same--;
int lpos=l;
int rpos=mid+1;
for(int i=l;i<=r;i++){
if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
tree[dep+1][lpos++]=tree[dep][i];
else if(tree[dep][i]==sorted[mid]&&same>0){
tree[dep+1][lpos++]=tree[dep][i];
same--;
}
else //比中间值大分入右边
tree[dep+1][rpos++]=tree[dep][i];
toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数
}
for (int i=l;i<=r;i++) sum[dep+1][i]=sum[dep+1][i-1]+tree[dep+1][i];
build(l, mid, dep + 1);
build(mid+1, r, dep+1);
}
ll query(int L,int R,int l,int r,int dep,int k){
if(l==r)return tree[dep][l];
int mid=(L+R)>>1;
int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左边的个数
if(cnt>=k){
//L+要查询的区间前被放在左边的个数
int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
//左端点加上查询区间会被放在左边的个数
int newr=newl+cnt-1;
return query(L,mid,newl,newr,dep+1,k);
}
else{
int newr=r+toleft[dep][R]-toleft[dep][r];
int newl=newr-(r-l-cnt);
int x0=toleft[dep][l-1]-toleft[dep][L-1];
return sum[dep+1][L+x0-1+cnt]-sum[dep+1][L+x0-1]+query(mid+1,R,newl,newr,dep+1,k-cnt);
}
}
int main(){
int Q,n;
ll m;
scanf("%d",&Q);
while(Q--){
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sorted[i]=a[i];
}
init(1,n);
sort(sorted+1,sorted+1+n);
build(1,n,0);
for(int i=1;i<=n;i++){
int l=0,r=i;
while(l+1<r){
int mid=l+r>>1;
if(query(1,n,1,i-1,0,mid)<=m-a[i]) l=mid;
else r=mid;
}
printf("%d ",i-r);
}
printf("\n");
}
return 0;
}