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
",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
",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)) 处理出一个数对 (pleq 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
",ans[i][1]-ans[i][0]);
}
} signed main() {Luitaryi::main(); return 0;}
B1 我菜爆了 100pts
我打表才看出来DP(递推)式子。。。
实际上可以理解为在上一个序列的最前面依次插了(1,2,3cdots n),他会与后面的 (n-1) 项产生 (0,1,2cdots n-1) 个逆序对
所以 (f[n][k]) 可以继承 (sum_{max(0,k-n+1)leq ileq 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
",(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
",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
",ans);
}
}
}
} signed main() {Luitaryi::main(); return 0;}
总结
做的题挺多,却不总结。
一定改。