敲日期的时候才发现今天是双 11 11 11。。
签到题。
排序后 upper_bound 一下即可。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include
using namespace std;
typedef long long ll;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=2e5+5;
int n,S,a[N];
int main(){
n=in(),S=in();
for(int i=1;i<=n;++i) a[i]=in();
sort(a+1,a+n+1);
ll ans=0;
for(int i=1;i<=n;++i){
if(a[i]>=S) break;
int pos=upper_bound(a+1,a+i,S-a[i])-a;
ans+=pos-1;
}
printf("%lld\n",ans);
return 0;
}
考场上直接打了个表,发现答案是:
( ⌊ n + m 2 ⌋ m ) \binom{\lfloor\frac{n+m}2\rfloor}{m} (m⌊2n+m⌋)
也可以先写一个 O ( n 2 ) O(n^2) O(n2) 的 d p dp dp 暴力,通过那个想正解。
这里是 std 的推导:
设选出来的数从小到大是 a 1 , a 2 , ⋯ , a m a_1,a_2,\cdots,a_m a1,a2,⋯,am,考虑令 b i = a i + i b_i=a_i+i bi=ai+i。发现 b i b_i bi 全是偶数且互不相同,那么一个 b b b 就唯一对应着一个 a a a,所以问题就变成了从 n + m n+m n+m 的偶数中选出 m m m 个的方案数。
时间复杂度 O ( T ) O(T) O(T)。
#include
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=2e6+5,P=998244353;
int n,m,fac[N],ifac[N];
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
int Inv(int x) {return power(x,P-2);}
void prework(){
fac[0]=fac[1]=1;
for(int i=2;i<N;++i) fac[i]=mul(fac[i-1],i);
ifac[N-1]=Inv(fac[N-1]);
for(int i=N-2;~i;--i) ifac[i]=mul(ifac[i+1],i+1);
}
int C(int n,int m) {return n<m?0:mul(fac[n],mul(ifac[m],ifac[n-m]));}
int main(){
prework();
int T=in();
while(T--){
n=in(),m=in();
printf("%d\n",C((n+m)>>1,m));
}
return 0;
}
一道大力分类讨论的题。
设询问区间是 [ x , y ] [x,y] [x,y],对于线段树的节点 [ l , r ] [l,r] [l,r],相当于统计多少个 [ x , y ] [x,y] [x,y] 的子区间会影响 [ l , r ] [l,r] [l,r]。
发现前两个在线段树上只有 O ( log n ) O(\log n) O(logn) 个点,可以暴力统计。但是情况 3 3 3 是有 O ( log n ) O(\log n) O(logn) 个子树,暴力会 T。
如果把 x , y x,y x,y 当做未知数,情况 3 3 3 的贡献式子会化成 A x 2 + B x y + C y 2 + D x + E y + F Ax^2+Bxy+Cy^2+Dx+Ey+F Ax2+Bxy+Cy2+Dx+Ey+F 的形式,可以在线段树上维护这些系数,把子树的答案都加到父亲上来,统计的时候把 x , y x,y x,y 具体的值代进去即可。
时间复杂度 O ( q log n ) O(q\log n) O(qlogn)。
#include
using namespace std;
typedef long long ll;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int gi() {return Read<int>();}
inline ll gl() {return Read<ll >();}
}
using IO::gi;
using IO::gl;
const int N=5e5+5;
int n,q,op;
ll l,r,sum,last=0;
namespace SGT{
struct Seg{ll sze,B,D,E,F;}T[N<<2];
void pushup(int root){
T[root].B+=T[root<<1].B+T[root<<1|1].B;
T[root].D+=T[root<<1].D+T[root<<1|1].D;
T[root].E+=T[root<<1].E+T[root<<1|1].E;
T[root].F+=T[root<<1].F+T[root<<1|1].F;
T[root].sze+=T[root<<1].sze+T[root<<1|1].sze;
}
#define mid ((l+r)>>1)
void build(int root,int l,int r){
T[root].sze=1;
T[root].D=2*l+1,T[root].E=2*r-1,T[root].F=-(ll)l*l-(ll)r*r-l+r;
if(l==r) return;
build(root<<1,l,mid),build(root<<1|1,mid+1,r);
T[root].B+=4,T[root].D+=-4*r+4,T[root].E+=-4*l-4,T[root].F+=4ll*l*r-4*l+4*r-4;
pushup(root);
}
ll Query(int root,int l,int r,int x,int y){
if(l>=x&&r<=y){
ll ans=0;
ans+=(T[root].B*x*y+T[root].D*x+T[root].E*y+T[root].F+(sum-(ll)x*x-(ll)y*y)*T[root].sze);
return ans;
}
ll ans=sum;
if(l>=x) ans-=(ll)(l-x)*(l-x+1);
if(r<=y) ans-=(ll)(y-r)*(y-r+1);
if(x<=mid) ans+=Query(root<<1,l,mid,x,y);
if(y> mid) ans+=Query(root<<1|1,mid+1,r,x,y);
return ans;
}
#undef mid
}
void Get(ll &l,ll &r){
l=(gl()^(last*op))%n+1,r=(gl()^(last*op))%n+1;
if(l>r) swap(l,r);
}
int main(){
n=gi(),q=gi(),op=gi();
SGT::build(1,1,n);
while(q--){
Get(l,r),sum=(ll)(r-l+1)*(r-l+2);
printf("%lld\n",last=SGT::Query(1,1,n,l,r)/2);
}
return 0;
}