6.25联考题解

版权声明:...............转载说一声并注明出处qaq............... https://blog.csdn.net/L_0_Forever_LF/article/details/80820259

A:
维护一个集合,兹磁插入一个数x,询问集合里的数和xand,or,xor运算的最大值
权值ai<65536=216 因为位运算不同位之间互相独立,我们把一个数拆成前8位和后8位,枚举前8位dp后8位以平衡复杂度
f[a][b]表示集合中前8位的数是a的数里,和一个后8位是b的数做位运算,后8位结果的最大值
x的前8位是x,后8位是y
插入x时枚举b更新f[x][b]
询问时,枚举a,用f[a][y]更新答案

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;
const int maxb = 256;

int n,opt,type;
int f[maxb][maxb],g[maxb][maxb];

char str[110];
int main()
{
    freopen("binary.in","r",stdin);
    freopen("binary.out","w",stdout);

    scanf("%d%s%d",&n,str,&type);
    opt=str[0]=='a'?0:(str[0]=='o'?1:2);

    for(int i=1;i<=n;i++)
    {
        int x; scanf("%d",&x); int y=x&255; x^=y;
        if(i>1)
        {
            int nowmax=0,nowsum=0;
            for(int j=0;j<maxb;j++)
            {
                int cx=f[j][y],cs=g[j][y];
                if(!cs) continue;
                int cj=j<<8;
                cx|=opt==0?(cj&x):(opt==1?(cj|x):(cj^x));
                if(cx>nowmax) nowmax=cx,nowsum=0;
                if(cx==nowmax) nowsum+=cs;
            }
            if(type) printf("%d %d\n",nowmax,nowsum);
            else printf("%d\n",nowmax);
        }

        x>>=8;
        for(int j=0;j<maxb;j++)
        {
            int cx=opt==0?(y&j):(opt==1?(y|j):(y^j));
            if(f[x][j]<cx) f[x][j]=cx,g[x][j]=0;
            if(f[x][j]==cx) g[x][j]++;
        }
    }

    return 0;
}

B:
一开始有个空串,n次操作,每次操作形如在第i次操作后的串尾添加一个字符x并且询问操作完的串最小周期p的长度,强制在线
对于串S,有p=|S|border,即他的最小周期=串长减最长的border(border就是一段和后缀相等的前缀)
于是考虑怎么求这个串的border,假设我们对这个单串建AC自动机,发现border就是这个串的fail的长度
问题变成在线维护一个AC自动机,要求每个点只能fail到他的祖先上
加入一个点时如果我们暴力去跳fail显然会T,于是我们要对每个节点维护一个son[26],表示在他后面接上字符c,会跳到哪个节点

那这个东西要怎么在线维护呢
考虑在某个点x后面接上一个字符c后我们要更新哪些信息
我们要新建一个点 y 代表在 x 后面接上字符c后的串
然后更新xson,维护yfailysonylen
这个东西显然只能可持久化维护,但是注意到如果我们更新了xson,因为可持久化我们会新建一个x,那么我们这个串上所有原来son指向x的都要更新,指向x,这样子我们单次插入更新的信息量十分巨大完全无法承受
所以我们要改一下sonfail的定义,改成指向原来应该指向的点代表的串的长度len,比如原来son[a]fail[b]都指向点c,现在他们要指向len[c]
然后我们再对每个点可持久化每个长度len下标对应的点id[len]
这样我们插入c的时候,只要更新xson,用fail[x]维护fail[y]son[y]就继承son[fail[y]],再维护yid就行了

复杂度O(nlogn)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 610000;
const int maxp = maxn*25;

int n,m,type;
struct segment
{
    int cnt;
    int seg[maxp],lc[maxp],rc[maxp];
    int loc,c;
    int newnode(int x)
    {
        ++cnt;
        lc[cnt]=lc[x],rc[cnt]=rc[x];
        seg[cnt]=seg[x];
        return cnt;
    }
    void upd(int &x,const int l,const int r)
    {
        x=newnode(x);
        if(l==r){ seg[x]=c;return; }
        int mid=(l+r)>>1;
        loc<=mid?upd(lc[x],l,mid):upd(rc[x],mid+1,r);
    }
    int q(int x,const int l,const int r)
    {
        if(l==r) return seg[x];
        int mid=(l+r)>>1;
        return loc<=mid?q(lc[x],l,mid):q(rc[x],mid+1,r);
    }
}Son,P;
int tot,fl[maxn],len[maxn],rootp[maxn],rootson[maxn];
int stri[maxn];
int ans;

void add(int i,int x,int c)
{
    x=stri[x];
    int now=++tot; stri[i]=now;
    len[now]=len[x]+1;

    int y=++tot; fl[y]=fl[x],len[y]=len[x],rootp[y]=rootp[x],rootson[y]=rootson[x];
    P.loc=len[y],P.c=y,P.upd(rootp[y],0,n);

    P.loc=fl[y]; int tmp=P.q(rootp[y],0,n);
    Son.loc=c; tmp=Son.q(rootson[tmp],1,m);
    P.loc=tmp; tmp=P.q(rootp[y],0,n);

    Son.loc=c,Son.c=len[now],Son.upd(rootson[y],1,m);

    fl[now]=len[tmp];
    rootp[now]=rootp[y];
    P.loc=len[now],P.c=now,P.upd(rootp[now],0,n);
    rootson[now]=rootson[tmp];

    //P.loc=0,printf("%d\n",P.q(rootp[now],0,n));
    //Son.loc=1; int temp=Son.q(rootson[now],1,m);
    //int ii=1;
}

int main()
{
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);

    read(n); read(m); read(type);
    for(int i=1;i<=n;i++)
    {
        int x,c; read(x); read(c);
        x^=ans,c^=ans;

        add(i,x,c);
        printf("%d\n",ans=(len[stri[i]]-fl[stri[i]]));
        ans*=type;
    }

    return 0;
}

C:

显然要对每个k,预处理出ans[k]表示有多少个(x,y),满足f(x,y)=k
如果暴力的做,就是枚举x,然后以x为根,dfs一遍整棵树,我们将(视为1,)视为-1,如果S(x,y)满足任意前缀非负任意后缀非正,然后我们就可以O(n2)的处理出这个东西
要加速这个暴力的话,考虑点分治,对每个分治中心x往下dfs下去分别搜可以作为左半边的串和可以作为右半边的串,比如左半边的串,要求从xy,任意后缀非负,且和为正,设和为s,拿他和右半边的和为s的那些串合并,合并的时候可以用FFT加速
因为对于每个分治中心去计算答案是,设当前树的大小是n的,合并时多项式的次数和是O(n)的,这次计算的复杂度是O(nlogn)
所以总复杂度是O(nlog2n)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define pb push_back
#define SZ(x) (int)x.size()
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void up(int &a,const int &b){if(a<b)a=b;}
const int maxn = 210000;
const double pi = acos(-1);

namespace FFT
{
    struct E
    {
        double x,y;
        friend inline E operator +(const E &x,const E &y){return (E){x.x+y.x,x.y+y.y};}
        friend inline E operator -(const E &x,const E &y){return (E){x.x-y.x,x.y-y.y};}
        friend inline E operator *(const E &x,const E &y){return (E){x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x};}
    }s1[maxn],s2[maxn];
    int N,id[maxn],ln;
    void dft(E f[],int sig)
    {
        for(int i=0;i<N;i++) if(i<id[i]) swap(f[i],f[id[i]]);
        for(int m=2;m<=N;m<<=1)
        {
            int t=m>>1;
            E w=(E){cos(2*pi/m),sin(2*pi*sig/m)};
            for(int j=0;j<N;j+=m)
            {
                E wn=(E){1,0};
                for(int i=0;i<t;i++)
                {
                    E tx=f[j+i],ty=f[j+i+t]*wn;
                    f[j+i]=tx+ty;
                    f[j+i+t]=tx-ty;
                    wn=wn*w;
                }
            }
        }
        if(sig==-1)
        {
            for(int i=0;i<N;i++) f[i].x/=(double)N;
        }
    }
    void solve(int f[],int g[],int fn,int gn)
    {
        N=1,ln=0;
        while(N<=fn+gn) N<<=1,ln++;
        for(int i=1;i<N;i++) id[i]=id[i>>1]>>1|(i&1)<<(ln-1);

        for(int i=0;i<=fn;i++) s1[i]=(E){(double)f[i],0};
        for(int i=fn+1;i<N;i++) s1[i]=(E){0,0};

        for(int i=0;i<=gn;i++) s2[i]=(E){(double)g[i],0};
        for(int i=gn+1;i<N;i++) s2[i]=(E){0,0};

        dft(s1,1); dft(s2,1);
        for(int i=0;i<N;i++) s1[i]=s1[i]*s2[i];
        dft(s1,-1);

        for(int i=0;i<N;i++) f[i]=(int)(s1[i].x+0.5);
    }
}

int n,m;
int c[maxn];
ll ans[maxn];
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn],v[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

int siz[maxn];
int findrt(int x,int fa,int N)
{
    siz[x]=1; int mxs=0;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(!v[k]&&y!=fa)
    {
        int re=findrt(y,x,N); if(re) return re;
        siz[x]+=siz[y];
        up(mxs,siz[y]);
    }
    if(mxs*2<=N&&(N-siz[x])*2<=N) return x;
    return 0;
}
vector<int>Vl[maxn],Vr[maxn];
int lx,rx;
void dfsl(int x,int fa,int sum,int mx,int now)
{
    siz[x]=1;

    sum+=c[x]; up(lx,sum);
    if(sum>mx) mx=sum,now=-1;
    if(sum==mx) Vl[sum].pb(++now);

    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa&&!v[k])
        dfsl(y,x,sum,mx,now),siz[x]+=siz[y];
}
void dfsr(int x,int fa,int sum,int mn,int now)
{
    sum+=c[x]; up(rx,-sum);
    if(sum<mn) mn=sum,now=-1;
    if(sum==mn) Vr[-sum].pb(++now);

    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa&&!v[k])
        dfsr(y,x,sum,mn,now);
}
int f[maxn],g[maxn],fn,gn;
void calc(int sig)
{
    for(int i=0,ui=min(lx,rx);i<=ui;i++)
    {
        for(int j=0;j<=fn+gn;j++) f[j]=g[j]=0;
        fn=gn=0;

        for(int j=0;j<SZ(Vl[i]);j++)
        {
            int x=Vl[i][j]; 
            f[x]++; up(fn,x);
        }
        for(int j=0;j<SZ(Vr[i]);j++)
        {
            int x=Vr[i][j];
            g[x]++; up(gn,x);
        }
        FFT::solve(f,g,fn,gn);
        for(int j=0;j<=fn+gn;j++) ans[j+(i>0)]+=f[j]*sig;
    }
}
void Divide_And_Conquer(int x,int N)
{
    x=findrt(x,0,N);
    while(lx>=0) Vl[lx--].clear();
    while(rx>=0) Vr[rx--].clear();
    dfsl(x,0,0,0,0);
    Vr[rx=0].pb(0);
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(!v[k])
        dfsr(y,x,0,0,0);
    calc(1);

    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(!v[k])
    {
        while(lx>=0) Vl[lx--].clear();
        while(rx>=0) Vr[rx--].clear();
        dfsl(y,x,c[x],max(c[x],0),0);
        dfsr(y,x,0,0,0);
        calc(-1);
    }
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(!v[k])
    {
        v[k]=v[k^1]=1;
        Divide_And_Conquer(y,siz[y]);
    }
}

char str[110];
int main()
{
    freopen("inverse.in","r",stdin);
    freopen("inverse.out","w",stdout);

    len=1;

    read(n);
    for(int i=1;i<n;i++)
    {
        int x,y; read(x),read(y);
        ins(x,y),ins(y,x);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str);
        c[i]=str[0]=='('?1:-1;
    }

    Divide_And_Conquer(1,n);

    read(m);
    while(m--)
    {
        int x; read(x);
        printf("%lld\n",ans[x]);
    }

    return 0;
}
阅读更多

没有更多推荐了,返回首页