来自FallDream的博客,未经允许,请勿转载,谢谢。
不得不说厦一的机子还是快 (其实数据也蛮水的) 交了两个暴力 一个骗分(我不会告诉你特判都写挂了的) 居然拿到了95+100+10
A.T组数据,每次给你n个数ai(T<=15,n<=40,ai<=5e7) 你要选出一些数使得总和大等于m且总和最小。
很容易想到双向宽搜 但是对接的时候要把两个2^20大小的数组排序,所以写一个归并/基数排序就行啦。
反正我是没想到 写了一个挺玄学的 把权值分段存到vector里面 然后暴力查...本机根本跑不过,随机数据大概1.3s,评测的时候只RE了一个点,好几个点1.000s卡过(稳健)
我的sb做法
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> #define MN 500001 #define INF 2100000000 #define MX 1000000000 #define getchar() (*S++) char B[1<<15],*S=B; using namespace std; inline int read() { int x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } vector<int> V[MN+5]; int ans,ne[MN+5],T,n,m,tot,lim,a[45],mn[MN+5]; inline void R(int&x,int y){y<x?x=y:0;} inline void Solve(int x) { if(x>=m){ans=min(ans,x);return;} if(x>m+MX) return; int num=ne[(m-x)/2000]; if(num!=-1) { if(mn[num]>=m-x) ans=min(ans,mn[num]+x); else if(mn[num]+x<ans) { for(int i=0;i<V[num].size();++i) V[num][i]+x>=m?(R(ans,V[num][i]+x),0):0; } } } void Dfs1(int x,int v) { if(x>lim) {int vv=v/2000;V[vv].push_back(v);R(mn[vv],v);return;} Dfs1(x+1,v);if(v<m)Dfs1(x+1,v+a[x]); } void Dfs2(int x,int v) { if(x==lim){Solve(v);return;} Dfs2(x-1,v);if(v<m)Dfs2(x-1,v+a[x]); } void Dfs3(int x,int v) { if(x>lim) {ne[v]=v;return;} Dfs3(x+1,v);if(v<m)Dfs3(x+1,v+a[x]); } void Dfs4(int x,int v) { if(x==lim){ans=min(ans,v>=m?v:(v+ne[m-v]));return;} Dfs4(x-1,v);if(v<m)Dfs4(x-1,v+a[x]); } void Solve1() { for(int i=0;i<=MN;++i) ne[i]=0; lim=n/2; Dfs3(1,0); for(int i=MN,last=INF;~i;--i) { if(ne[i]) last=i; ne[i]=last; } Dfs4(n,0); } void Solve2() { for(register int i=0;i<=MN;i+=2) V[i].clear(),mn[i]=INF,V[i+1].clear(),mn[i+1]=INF; lim=n/2;Dfs1(1,0); for(register int i=MN,last=-1;~i;--i) V[i].size()?last=i:0,ne[i]=last; Dfs2(n,0); } int main() { freopen("challenge.in","r",stdin); freopen("challenge.out","w",stdout); fread(B,1,1<<15,stdin); for(T=read();T;--T) { n=read();m=read();ans=INF;tot=0; for(register int i=1;i<=n;++i) tot+=(a[i]=read()); sort(a+1,a+n+1); if(tot<m) {puts("-1");continue;} if(tot<=MN) Solve1(); else Solve2(); printf("%d ",ans); } return 0; }
正解的靠谱做法
#include<iostream> #include<cstdio> #include<cstring> #define rint register int #define INF 2000000000 #define MN 1048600 #define N 32768 using namespace std; inline int read() { int x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } int a[MN+5],b[MN+5],s[45],top1,top2,T,m,n,ans,lim,tot=0,v[N+5],v2[N+5],sa[MN+5]; void Dfs2(int x,int v) { if(x==lim){b[++top2]=v;return;} Dfs2(x-1,v);if(v<m) Dfs2(x-1,v+s[x]); } void Dfs1(int x,int v) { if(x>lim) {a[++top1]=v;return;} Dfs1(x+1,v);if(v<m) Dfs1(x+1,v+s[x]); } void Sort(int*s,int n) { for(rint i=0;i<N;++i) v[i]=v2[i]=0; for(rint i=1;i<=n;++i) ++v2[s[i]&(N-1)],++v[s[i]>>15]; for(rint i=1;i<N;++i) v[i]+=v[i-1],v2[i]+=v2[i-1]; for(rint i=1;i<=n;++i) sa[v2[s[i]&(N-1)]--]=s[i]; for(rint i=n;i;--i) s[v[sa[i]>>15]--]=sa[i]; } int main() { freopen("challenge.in","r",stdin); freopen("challenge.out","w",stdout); for(T=read();T;--T) { n=read();m=read();ans=INF;tot=0; for(int i=1;i<=n;++i) tot+=(s[i]=read()); if(tot<m) {puts("-1");continue;} top1=top2=0;lim=n>>1; Dfs1(1,0);Dfs2(n,0); Sort(a,top1);Sort(b,top2); for(rint i=1,j=top2;i<=top1;++i) { while(j>1&&b[j-1]+a[i]>=m) --j; if(b[j]+a[i]>=m) ans=min(ans,b[j]+a[i]); } printf("%d ",ans); } return 0; }
B.什么三角函数乱求导之类的 不会
C.给一个由左右括号组成的序列,每次求一段区间的最长的合法括号序列的长度。 n,m<=30000
我写了一个n^2的做法 还会爆内存 居然水过了 而且跑的飞快(数据不会都是随机的吧= =)
std写了一个n根号log的做法 但是有个只有根号的做法..
就是先预处理每个括号最近的可以匹配的括号,然后显然答案就是由一些这样的序列接起来的。
考虑莫队,为了避免删除操作,考虑块内暴力,块外的话,每次计算左节点所在的块到右节点的答案,然后暴力移动到左节点计算答案
在右指针右移的时候,维护每个点能匹配的最左边的且在左节点所在的块的右边的节点;每找到一个右括号,假设它能匹配的最近做括号的位置是pos,就看看pos-1是否是右括号且已经被匹配了,是的话就把两段接起来,这样移动的复杂度显然是O(1)的 然后移动左节点的时候把它和右边接起来就行了。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #define MN 30000 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n,m,size,block[MN+5],L[MN+5],Q[MN+5],top=0,Ans,R[MN+5],Res[MN+5],l,r,lt[MN+5],rt[MN+5]; struct ques{int l,r,id;}q[MN+5]; char st[MN+5]; bool cmp(ques x,ques y){return block[x.l]==block[y.l]?x.r<y.r:x.l<y.l;} int main() { freopen("spell.in","r",stdin); freopen("spell.out","w",stdout); n=read();m=read();size=sqrt(n); scanf("%s",st+1); for(int i=1;i<=n;++i) block[i]=(i-1)/size+1; for(int i=1;i<=n;++i) if(st[i]=='(') Q[++top]=i; else if(top) L[i]=Q[top--]; top=0; for(int i=n;i;--i) if(st[i]==')') Q[++top]=i; else if(top) R[i]=Q[top--]; for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i; sort(q+1,q+m+1,cmp); for(int i=1,last=0;i<=m;++i) { if(last!=block[q[i].l]) { memset(lt,0,sizeof(lt)); memset(rt,0,sizeof(rt)); l=r=block[q[i].l]*size+1; last=block[q[i].l];Ans=0; } if(block[q[i].r]==block[q[i].l]) { int ans=0;top=0; for(int j=q[i].l;j<=q[i].r;++j) if(st[j]=='(') Q[++top]=j,lt[j]=0; else if(top) { lt[j]=(Q[top]>q[i].l&<[Q[top]-1])?lt[Q[top]-1]:Q[top]; --top,ans=max(ans,j-lt[j]+1); } else lt[j]=0; Res[q[i].id]=ans; } else { while(r<q[i].r) { ++r; if(L[r]>=l) { if(L[r]>l&<[L[r]-1]) lt[r]=lt[L[r]-1]; else lt[r]=L[r]; Ans=max(Ans,r-lt[r]+1);rt[lt[r]]=r; } } int ans=0; for(int j=l-1;j>=q[i].l;--j) if(R[j]&&R[j]<=r) { if(R[j]<l) rt[j]=rt[R[j]+1]?rt[R[j]+1]:R[j]; else rt[j]=R[j]; ans=max(ans,rt[j]-j+1); if(rt[j]>=l-1) ans=max(ans,rt[rt[j]+1]-j+1); } Res[q[i].id]=max(ans,Ans); } } for(int i=1;i<=m;i++) printf("%d ",Res[i]); return 0; }