6.25聯考題解

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章