20190919

A1

DP \(O(n^3)\)
其实不用枚举从谁开始跳。。。懒得改了

#include<bits/stdc++.h>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
  register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
  do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*f;
} const int N=51;
struct node { int c,h;
  inline bool operator < (const node& that) const {return h>that.h;}
}a[N];
int n,m,ans,f[N][N],d[N];
inline void main() {
  n=g(); for(R i=1;i<=n;++i) a[i].c=g();
  for(R i=1;i<=n;++i) a[i].h=g(); m=g();
  sort(a+1,a+n+1);
  for(R t=1;t<=n;++t) if(a[t].c<=m) { 
    memset(f,0x3f,sizeof f),f[t][1]=a[t].c,ans=max(ans,1);
    memset(d,0x3f,sizeof d),d[1]=a[t].c+a[t].h; 
    for(R i=t+1;i<=n;++i) {
      for(R j=i-t+1;j>=1;--j) {
        f[i][j]=d[j-1]-a[i].h+a[i].c;
        if(f[i][j]<=m) ans=max(ans,j);
        if(d[j]>f[i][j]+a[i].h) d[j]=f[i][j]+a[i].h;
      }
    }
  } printf("%d\n",ans);
}
} signed main() {Luitaryi::main(); return 0;}

A2

排好序后显然第一项是 \(a_1+a_2\),第二项是 \(a_1+a_3\),我们可以枚举 \(a_2+a_3\) ,然后显然除了前面的三项,此时最小是 \(a_1+a_4\),这时我们标记 \(a_2+a_4\) , \(a_3+a_4\),此时没有被标记中的最小的是 \(a_1+a_5\)。。。以此类推。
复杂度不到 \(O(n^3log(n))\)

#include<bits/stdc++.h>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
  register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
  do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*f;
} const int M=45000,N=301;
int n,m,cnt,mem[N],ans[N][N],a[M];
bool vis[M];
inline void ck(int p) {//枚举x2+x3 
  memset(vis,0,sizeof vis);
  if((a[1]+a[2]+a[p])&1) return ;//若是奇数,显然不能等于2*(x1+x2+x3)
  mem[1]=(a[1]+a[2]+a[p])/2-a[p]; 
  mem[2]=a[1]-mem[1],mem[3]=a[2]-mem[1];
  vis[1]=vis[2]=vis[p]=true;
  for(R p=4,q=3;p<=n;++p) { 
    while(q<=m&&vis[q]) ++q;
    if(q>m) return ; mem[p]=a[q]-mem[1]; vis[q]=true;
    for(R i=2;i<p;++i) { //检查解是否合法 
      if(mem[i]>mem[p]) return ; //有序
      R vl=mem[i]+mem[p]; R pos=lower_bound(a+1,a+m+1,vl)-a;
      if(a[pos]!=vl) return ;//没有对应的数 
      R tmp=pos; while(tmp<=m&&a[tmp]==a[pos]&&vis[tmp]) ++tmp;//找到最靠前且没有用过的
      if(a[tmp]!=a[pos]||vis[tmp]) return ;//有对应的数但是都用过
      pos=tmp,vis[pos]=true; 
    }
  } ++cnt; for(R i=1;i<=n;++i) ans[cnt][i]=mem[i]; 
}
inline void main() {
  n=g(),m=n*(n-1)/2; for(R i=1;i<=m;++i) a[i]=g();
  sort(a+1,a+m+1); for(R p=3;p<=m;) {
    ck(p); R q=p; while(q<=m&&a[p]==a[q]) ++q; p=q;//跳过一样的数 
  } printf("%d\n",cnt);
  for(R i=1;i<=cnt;++i,puts("")) for(R j=1;j<=n;++j) printf("%d ",ans[i][j]);
}
} signed main() {Luitaryi::main(); return 0;}

A3

套路,可是我不会。
把询问离线,看作两个前缀和相减;
考虑如何平衡修改与查询的复杂度,我们可以每次花 \(O(100)\) 处理出一个数对 \(p\leq 100\)时的答案,查询时直接使用答案。
\(p > 100\) ,他的倍数很少,我们可以直接查 \(k*p+v\) 出现了几次(之前开个桶)。

#include<bits/stdc++.h>
#define R register int
using namespace std;
namespace Luitaryi {
namespace Fread {
static char B[1<<15],*S=B,*T=B;
#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
inline int g() { R x=0,f=1;
  register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
  do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*f;
} 
} using Fread::g;
const int N=100010,L=10000,B=100;
struct node { int p,v,id; bool op; node() {}
  node(int _p,int _v,int _id,bool _op) {p=_p,v=_v,id=_id,op=_op;}
}vr[N<<1]; int n,m,a[N];
int cnt,s[L+10],c[B+10][B+10],nxt[N<<1],fir[N],ans[N][2];
inline void main() {
  n=g(),m=g(); for(R i=1;i<=n;++i) a[i]=g();
  for(R i=1;i<=m;++i) {
    R l=g(),r=g(),p=g(),v=g();
    vr[++cnt]=node(p,v,i,0),nxt[cnt]=fir[l-1],fir[l-1]=cnt;
    vr[++cnt]=node(p,v,i,1),nxt[cnt]=fir[r],fir[r]=cnt;
  } for(R u=1;u<=n;++u) { ++s[a[u]];
    for(R i=1;i<=B;++i) ++c[i][a[u]%i];
    for(R i=fir[u];i;i=nxt[i]) { R p=vr[i].p,v=vr[i].v;
      if(p<=B) ans[vr[i].id][vr[i].op]=c[p][v];
      else { R tmp=0;
        for(R i=v;i<=L;i+=p) tmp+=s[i];
        ans[vr[i].id][vr[i].op]=tmp;
      }
    }
  } for(R i=1;i<=m;++i) printf("%d\n",ans[i][1]-ans[i][0]);
}
} signed main() {Luitaryi::main(); return 0;}

B1 我菜爆了 100pts

我打表才看出来DP(递推)式子。。。
实际上可以理解为在上一个序列的最前面依次插了\(1,2,3\cdots n\),他会与后面的 \(n-1\) 项产生 \(0,1,2\cdots n-1\) 个逆序对
所以 \(f[n][k]\) 可以继承 \(\sum_{\max(0,k-n+1)\leq i\leq k}f[n-1][i]\)

#include<bits/stdc++.h>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
  register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
  do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*f;
} const int N=1000,M=10000;
int T,n,k;
int f[N+10][N+10],s[N+10][N+10];
inline void main() {
  f[1][0]=s[1][0]=s[1][1]=s[1][2]=1;
  for(R i=2;i<=N;++i) { 
    for(R j=0,lim=min(i*(i-1)/2,N);j<=lim;++j) {
      if(j-i>=0) f[i][j]=((s[i-1][j]-s[i-1][j-i])%M+M)%M;
      else f[i][j]=s[i-1][j];
    } s[i][0]=1;
    for(R j=1,lim=min(i*(i+1)/2,N);j<=lim;++j) 
      s[i][j]=(s[i][j-1]+f[i][j])%M;
  } T=g(); while(T--) {
    n=g(),k=g(); printf("%d\n",(f[n][k]%M+M)%M);
  }
}
} signed main() {Luitaryi::main(); return 0;}

B2 我被爆踩了 0pts

ST表两维写反(为什要用ST表)
还用了对顶堆的 \(n^2logn\) 的做法(并不用对顶堆,直接每个数向两边扫一下 \(n^2\) 就够了)
菜的真实。

#include<bits/stdc++.h>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
  register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
  do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*f;
} const int LEN=200,N=2010;
struct node { int p,w; node() {} node(int _p,int _w) {p=_p,w=_w;}
  inline bool operator < (const node& that) const {return w<that.w||(w==that.w&&p<that.p);}
  inline bool operator > (const node& that) const {return w>that.w||(w==that.w&&p>that.p);}
}; 
priority_queue<node> p;
priority_queue<node,vector<node>,greater<node> > q;
int n,m,a[N],s[N],mx[12][N],lg[N];
inline void build() {
  lg[1]=0; for(R i=2;i<=n;++i) lg[i]=lg[i>>1]+1; memcpy(mx[0],s,sizeof s);
  for(R t=1,lim=lg[n];t<=lim;++t) for(R i=1,lim=n-(1<<t)+1;i<=lim;++i) 
    mx[t][i]=max(mx[t-1][i],mx[t-1][i+(1<<t-1)]);
}
inline int qmx(int l,int r) {R t=lg[r-l+1]; return max(mx[t][l],mx[t][r-(1<<t)+1]);}
inline void main() {
  n=g(); for(R i=1;i<=n;++i) a[i]=g(); for(R i=1;i<=n;++i) {
    while(p.size()) p.pop(); while(q.size()) q.pop();
    for(R j=i;j<=n;j+=2) {
      p.push(node(j,a[j])); if(j-i) p.push(node(j-1,a[j-1])); register node tmp;
      while(p.size()>(j-i+2)/2) tmp=p.top(),p.pop(),q.push(tmp);
      if(q.size()&&p.top()>q.top()) {
        register node t=p.top(); tmp=q.top();
        p.pop(),q.pop(); p.push(tmp),q.push(t);
      } R pos=p.top().p; s[pos]=max(s[pos],j-i+1);
    }
  } build(); m=g(); while(m--) {
    R l=g(),r=g(); printf("%d\n",qmx(l,r));
  }
}
} signed main() {Luitaryi::main(); return 0;}

B3 又是套路但是我不会

缺乏总结。。
我们又要平衡复杂度。
我们定义 \(f[A][B]\) 为 二进制表示下 前8位位\(A\) 的子集,后8位为 \(B\) 的数的个数。
添加时枚举超集,查询时枚举子集。

#include<bits/stdc++.h>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
  register char ch; while(!isdigit(ch=getchar())) f=ch=='-'?-1:f;
  do x=x*10+(ch^48); while(isdigit(ch=getchar())); return x*f;
} const short N=1<<8,L=8,B=N-1;
int c[N+1][N+1],n;
inline void main() {
  n=g(); for(R i=1;i<=n;++i) { register char ch; 
    while(!isalpha(ch=getchar())); 
    R x=g(); if(ch=='a') { register short tmp=x&B,b=x>>L;
      for(register short i=x>>L;i<N;i=b|(i+1)) ++c[i][tmp];
    } if(ch=='d') { register short tmp=x&B,b=x>>L;;
      for(register short i=x>>L;i<N;i=b|(i+1)) --c[i][tmp];
    } if(ch=='c') { R ans=0; register short tmp=(x>>L)&B,b=x&B;
      for(register short i=x&B;i;i=b&(i-1)) {
        ans+=c[tmp][i]; 
      } ans+=c[tmp][0]; printf("%d\n",ans);
    }
  }
}
} signed main() {Luitaryi::main(); return 0;}

总结

做的题挺多,却不总结
一定改。

posted @ 2019-09-22 16:21  LuitaryiJack  阅读(175)  评论(0编辑  收藏  举报