【题目】

1004 Distribution of books

【题意】

n本书,分给k个人。可以丢掉一些后缀,使得权值和最大的一组人值最小

2019 Multi-University Training Contest 3_#define

【代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,k,tot;
ll a[N],s1[N],s[N];
int pre[N],mx[N*4];
int getid(ll x)
{
return lower_bound(s1+1,s1+1+tot,x)-s1;
}
void up(int id,int l,int r,int pos,int val)
{
if(l==r){
mx[id]=val;
return ;
}
int mid=l+r>>1;
if(pos<=mid) up(id<<1,l,mid,pos,val);
else up(id<<1|1,mid+1,r,pos,val);
mx[id]=max(mx[id<<1],mx[id<<1|1]);
}
int qu(int id,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return mx[id];
int mid=l+r>>1;
int res=0;
if(ql<=mid) res=max(res,qu(id<<1,l,mid,ql,qr));
if(qr>mid) res=max(res,qu(id<<1|1,mid+1,r,ql,qr));
return res;
}
bool valid(ll mid)
{
for(int i=0;i<=4*tot;++i) mx[i]=0;
rep(i,1,n)
{
ll val=s[i]-mid;
int ql=getid(s[i]-mid);
int v=qu(1,1,tot,ql,tot);
if (v == k - 1) {
if (v || (!v && s[i] <= mid))
return 1;
}
if (v || (!v && s[i] <= mid))
up(1, 1, tot, pre[i], v + 1);
}
return 0;
}
int main()
{
int _;
cin>>_;
while(_--)
{
scanf("%d%d",&n,&k);
rep(i,0,n) s1[i]=s[i]=0;
ll mn=1e9;
rep(i,1,n)
{
scanf("%lld",&a[i]);
s1[i]=s1[i-1]+a[i];
s[i]=s1[i];
mn=min(mn,a[i]);
}
sort(s1+1,s1+1+n);
tot=unique(s1+1,s1+1+n)-s1-1;
rep(i,1,n) pre[i]=getid(s[i]);
ll l,r,ans;

ll M=abs(mn*n);
l=0,r=M+1e9;
ans=l;
while(l<=r)
{
ll mid=l+r>>1;
if(valid(mid-M))
{
ans=mid,r=mid-1;
}
else l=mid+1;
}
printf("%lld\n",ans-M);
}
}

【题目】

1006 Fansblog

【题意】

给个n,问小于n的第一个素数的阶乘是多少?n<=1e14

【解法】

威尔逊定理,(p-1)!%p=p-1,p 是素数时。。。

求出p-1的阶乘,然后倒推,素数差距不会超过很大,暴力即可

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod;

bool isPrime( ll num )
{
if(num %6!= 1&&num %6!= 5)
return 0 ;
ll tmp =sqrt(num);
for(int i= 5; i <=tmp; i+=6 )
if(num %i== 0||num %(i+ 2)==0)
return 0 ;
return 1 ;
}

__int128 powmod(__int128 a,__int128 b) {ll res=1;a%=mod;
assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%lld",&mod);
ll p;
for(ll i=mod-1;i;i--)
if(isPrime(i)) {
p=i;break;
}
__int128 tmp=mod-1;
for(ll i=p+1;i<=mod-1;i++)
tmp=(tmp*powmod(i,mod-2))%mod;
ll t=tmp%mod;
printf("%lld\n",t);
}
}

【题目】

1007 Find the answer

【题意】

cf原题,n个权值,一个容量m,从一开始装,若当前装不下了,问最少去掉前面多少个人才能使自己可以装进容量

【解法】

典型权值线段树即可,树上二分

【代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N],b[N],id[N];
ll sum[4*N],num[4*N];
int n;
ll m;
int getid(ll x)
{
return lower_bound(b+1,b+1+n,x)-b;
}
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
ll qu(int id,int l,int r,ll val)
{
if(l==r){
ll flag=0;
if(val%b[l]) flag=1;
return val/b[l]+flag;
}
//if(sum[id]==val) return num[id];
int mid=l+r>>1;
ll ans=0;
if(val>sum[id<<1|1])
{
ans+=num[id<<1|1];
ans+= qu(id<<1,l,mid,val-sum[id<<1|1]);
}
else ans+= qu(id<<1|1,mid+1,r,val);
return ans;
}
void up(int id,int l,int r,int pos,ll val)
{
if(l==r)
{
num[id]++;
sum[id]+=val;
return ;
}
int mid=l+r>>1;
if(pos<=mid) up(id<<1,l,mid,pos,val);
else up(id<<1|1,mid+1,r,pos,val);
sum[id]=sum[id<<1]+sum[id<<1|1];
num[id]=num[id<<1]+num[id<<1|1];
}
int main()
{
int _;
_=input();
while(_--){
n=input();
m=input();
for(int i=0;i<=4*n+10;++i) sum[i]=num[i]=0;
rep(i,1,n)
{
a[i]=input();
b[i]=a[i];
}
sort(b+1,b+1+n);
rep(i,1,n) id[i]=getid(a[i]);
ll ss=0;
rep(i,1,n)
{
ll tt=0;
if(ss+a[i]>m)//若没有时间了
{
ll val=ss+a[i]-m;
tt=qu(1,1,n,val);
}
up(1,1,n,id[i],a[i]);
ss+=a[i];
printf("%lld ",tt);
}
puts("");
/*rep(i,1,n)
{
if(i!=n) printf("%lld ",ans[i]);
else printf("%lld\n",ans[i]);
}*/
}
}