[4.30四校联考]

来自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;
}
View Code

正解的靠谱做法

#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&&lt[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&&lt[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;
}
原文地址:https://www.cnblogs.com/FallDream/p/liankao430.html