zoukankan      html  css  js  c++  java
  • 十二省联考2019

    异或粽子

    Luogu
    LOJ
    BZOJ
    先做个前缀异或和求出(s_i),那么要做的就是找在序列中任选两个数异或的最大值。
    然后对每个(s_i)求出(maxlimits_{j}(s_ioperatorname{xor}s_j)),全部丢到一个堆里面。
    每次把堆里面的最大值取出来加入答案,假如这是(koperatorname{thmax}limits_j(s_ioperatorname{xor}s_j)),就把((k+1)operatorname{thmax}limits_j(s_ioperatorname{xor}s_j))丢进堆。
    具体可以用Trie树实现,时间复杂度为(O((n+k)(log a+log n)))
    另外这样求的是无序对,所以最开始要(kleftarrow 2k),最后(ansleftarrow frac{ans}2)

    #include<queue>
    #include<cctype>
    #include<cstdio>
    #include<utility>
    #include<algorithm>
    using u32=unsigned int;
    using i64=long long;
    using pi=std::pair<u32,u32>;
    const int N=500007;
    int tot,num[N];u32 s[N];i64 ans;char ibuf[1<<23|1],*iS=ibuf;
    struct node{int ch[2],size;}t[N<<4];std::priority_queue<pi>q;
    u32 read(){u32 x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    void insert(u32 x)
    {
        for(int p=0,i=31,d;~i;++t[p=t[p].ch[d]].size,--i) if(!t[p].ch[d=x>>i&1]) t[p].ch[d]=++tot;
    }
    u32 query(u32 x,int k)
    {
        u32 ans=0;
        for(int p=0,i=31,d;~i;--i) if(t[t[p].ch[!(d=x>>i&1)]].size<k) k-=t[t[p].ch[!d]].size,p=t[p].ch[d]; else ans|=((u32)1)<<i,p=t[p].ch[!d];
        return ans;
    }
    int main()
    {
        fread(ibuf,1,1<<23,stdin);
        int n=read(),k=read()*2;i64 ans=0;insert(0);
        for(int i=1;i<=n;++i) insert(s[i]=s[i-1]^read());
        for(int i=0;i<=n;++i) q.emplace(query(s[i],num[i]=1),i);
        for(int x;k;--k) ans+=q.top().first,x=q.top().second,q.pop(),q.emplace(query(s[x],++num[x]),x);
        printf("%lld",ans/2);
    }
    

    字符串问题

    Luogu
    LOJ
    (u)支配(v)则连边(A_u ightarrow B_v)
    (B_u)(A_v)的前缀则连边(B_u ightarrow A_v)
    那么我们要求的就是这张图的点权最长路,拓扑排序+dp即可。
    考虑如何优化建图。
    先建出反串的SAM。
    然后对于给定的子串(s_{l,r}),parent树上倍增找到它在SAM中的出现位置,然后把这个子串挂在SAM的这个点上。
    注意到SAM上一个节点中的字符串在反串中互为后缀,因此它们在原串中互为前缀。
    那么我们以结束位置为第一关键字,是否为(A)类子串(是为(1),不是为(0))为第二关键字对每个节点上的子串降序排序。
    这样每一个(B)子串类子串要连向的就是这个节点上排在它后面的(A)类子串。
    注意到我们要求的是最长路,因此只需要向后面第一个(A)类子串连边即可。

    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    namespace IO
    {
        char ibuf[(1<<26)+1],*iS=ibuf;
        void In(){fread(ibuf,1,1<<26,stdin);}
        int read(){int x=0;while(!isdigit(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
        void gets(char*s){while(isspace(*iS))++iS;while(islower(*iS))*s++=*iS++;*s='';}
    }
    using i64=long long;
    using IO::read;
    const int N=200007;
    int cnt,tot,now,link[N*2],len[N*4],trans[N*2][26],id[N],fa[20][N*2],a[N],b[N],is[N*4],las[N*2],in[N*4];
    char str[N];i64 dis[N*4];
    std::vector<int>e[N*4],g[N*2];std::queue<int>q;
    void clear()
    {
        for(int i=0;i<20;++i) memset(fa[i]+1,0,cnt<<2);
        for(int i=1;i<=cnt;++i) g[i].clear();
        for(int i=1;i<=tot;++i) e[i].clear();
        memset(trans[1],0,cnt*104),memset(is+1,0,tot<<2),memset(in+1,0,tot<<2),memset(dis+1,0,tot<<3),cnt=now=1;
    }
    void extend(int c)
    {
        int p=now,q;len[now=++cnt]=len[p]+1;
        for(;p&&!trans[p][c];p=link[p]) trans[p][c]=now;
        if(!p) return link[now]=1,void();
        if(len[q=trans[p][c]]==len[p]+1) return link[now]=q,void();
        len[++cnt]=len[p]+1,memcpy(trans[cnt],trans[q],104),link[cnt]=link[q],link[q]=link[now]=cnt;
        for(;p&&trans[p][c]==q;p=link[p]) trans[p][c]=cnt;
    }
    void getin(int flg)
    {
        int l=read(),r=read()-l+1;l=id[l];
        for(int i=19;~i;--i) if(fa[i][l]&&len[fa[i][l]]>=r) l=fa[i][l];
        is[++tot]=flg,len[tot]=r,g[l].push_back(tot);
    }
    i64 toposort()
    {
        i64 ans=0;
        for(int i=1;i<=tot;++i) if(!in[i]) q.push(i);
        for(int u;!q.empty();)
        {
    	u=q.front(),q.pop(),ans=std::max(ans,dis[u]+len[u]);
    	for(int v:e[u]) if(dis[v]=std::max(dis[v],dis[u]+len[u]),!--in[v]) q.push(v);
        }
        for(int i=1;i<=tot;++i) if(in[i]) return -1;
        return ans;
    }
    int main()
    {
        IO::In();
        for(int T=read(),n,m,na,nb;T;--T)
        {
    	clear(),IO::gets(str+1),n=strlen(str+1);
    	for(int i=n;i;--i) extend(str[i]-'a'),id[i]=now;
    	memcpy(fa[0]+1,link+1,cnt<<2),tot=cnt;
    	for(int j=1;j<20;++j) for(int i=1;i<=cnt;++i) fa[j][i]=fa[j-1][fa[j-1][i]];
    	na=read();for(int i=1;i<=na;++i) getin(1),a[i]=tot;
    	nb=read();for(int i=1;i<=nb;++i) getin(0),b[i]=tot;
    	for(int i=1;i<=cnt;++i) std::sort(g[i].begin(),g[i].end(),[](int i,int j){return len[i]<len[j]||(len[i]==len[j]&&is[i]<is[j]);});
    	for(int i=1;i<=cnt;++i)
    	{
    	    int u=i;
    	    for(int v:g[i]) if(e[u].push_back(v),++in[v],!is[v]) u=v;
    	    las[i]=u;
    	}
    	for(int i=2;i<=cnt;++i) e[las[link[i]]].push_back(i),++in[i];
    	for(int i=1;i<=tot;++i) if(!is[i]) len[i]=0;
    	m=read();for(int i=1,u,v;i<=m;++i) u=read(),v=read(),e[a[u]].push_back(b[v]),++in[b[v]];
    	printf("%lld
    ",toposort());
        }
    }
    

    皮配

    Luogu
    LOJ
    BZOJ
    我们称有不喜欢的老师的学校为“刁的学校”,有刁的学校的城市为“刁的城市”。
    考虑(k=0)的情况,此时阵营和派系是独立的,我们可以分别把城市/学校当做物品做背包得到(f_i,g_i),其中(f_i/g_i)分别表示有(i)名选手选择蓝阵营/鸭派系的方案数。那么(c)名选手在蓝阵营,(d)名选手在鸭派系的方案数为(sumlimits_{i=c-C_1}^{C_0}f_isumlimits_{j=d-C_0}^{C_1}g_i)
    现在有刁的学校和城市,因此先计算出不刁的城市及其学校的(f,g)
    然后考虑计算刁的城市/学校的方案数,设(F_{i,j})表示有(i)名选手选择蓝阵营,(j)名选手选择鸭派系的方案数,然后枚举刁的城市,再枚举刁的城市中的学校,根据讨厌的限制进行dp即可。
    最后再枚举刁的城市中有多少选手选择蓝阵营,多少选手选择鸭派系即可求出答案。
    时间复杂度为(O((n+c)m+kmmin(ks,m)))

    #include<cstdio>
    #include<cstring>
    #include<numeric>
    #include<algorithm>
    const int N=1007,M=2507,P=998244353;
    int read(){int x;scanf("%d",&x);return x;}
    void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
    int add(int a,int b){return a+=b-P,a+(a>>31&P);}
    int sub(int a,int b){return a-=b,a+(a>>31&P);}
    struct City{int sum,have_hate;}cy[N];
    struct School{int bel,sum,hate;}sl[N];
    int f[M],g[M],F[M][M],G[M][M];
    int cal(int*s,int l,int r){return l>r? 0:sub(s[r],l? s[l-1]:0);}
    void solve()
    {
        int n=read(),c=read(),c0=read(),c1=read(),d0=read(),d1=read(),cs=0,ds=0,all=0,ans=0;
        memset(cy+1,0,8*c),memset(f,0,4*(c0+1)),memset(g,0,4*(d0+1)),memset(F,0,sizeof F),F[0][0]=f[0]=g[0]=1;
        for(int i=1;i<=n;++i) sl[i]={read(),read(),-1},cy[sl[i].bel].sum+=sl[i].sum,all+=sl[i].sum;
        for(int k=read(),x;k;--k) x=read(),sl[x].hate=read(),cy[sl[x].bel].have_hate=1;
        for(int i=1;i<=c;++i) if(!cy[i].have_hate&&cy[i].sum) for(int j=c0,t=cy[i].sum;j>=t;--j) inc(f[j],f[j-t]);
        for(int i=1;i<=n;++i) if(!~sl[i].hate) for(int j=d0,t=sl[i].sum;j>=t;--j) inc(g[j],g[j-t]);
        std::partial_sum(f,f+c0+1,f,add),std::partial_sum(g,g+d0+1,g,add);
        for(int city=1;city<=c;++city)
            if(cy[city].have_hate)
    	{
                cs=std::min(cs+cy[city].sum,c0),memcpy(G,F,sizeof F);
                for(int school=1,t;school<=n;++school)
    		if(sl[school].bel==city&&~sl[school].hate)
    		{
    		    ds=std::min(ds+(t=sl[school].sum),d0);
                        if(sl[school].hate==1) for(int i=0;i<=cs;++i) memmove(F[i]+t,F[i],4*std::max(0,ds-t+1)),memset(F[i],0,4*t);
                        if(sl[school].hate>=2) for(int i=0;i<=cs;++i) for(int j=ds;j>=t;--j) inc(F[i][j],F[i][j-t]);
    		    if(sl[school].hate==3) for(int i=0;i<=cs;++i) memmove(G[i]+t,G[i],4*std::max(0,ds-t+1)),memset(G[i],0,4*t);
    		    if(sl[school].hate<=1) for(int i=0;i<=cs;++i) for(int j=ds;j>=t;--j) inc(G[i][j],G[i][j-t]);
                    }
    	    for(int j=0,t=cy[city].sum;j<=ds;++j)
    	    {
    		for(int i=cs;i>=t;--i) F[i][j]=F[i-t][j];
    		for(int i=t-1;~i;--i) F[i][j]=0;
    	    }
    	    for(int i=0;i<=cs;++i) for(int j=0;j<=ds;++j) inc(F[i][j],G[i][j]);
            }
        for(int i=0;i<=cs;++i) for(int j=0;j<=ds;++j) inc(ans,1ll*cal(f,std::max(0,all-c1-i),c0-i)*cal(g,std::max(0,all-d1-j),d0-j)%P*F[i][j]%P);
        printf("%d
    ",ans);
    }
    int main(){for(int T=read();T;--T)solve();}
    

    春节十二响

    Luogu
    LOJ
    BZOJ
    考虑每个点维护一个堆表示该点分配的各个内存段大小。
    我们知道一个点的儿子的子树的内存段是可以合并的,所以我们考虑从下往上把每个节点的儿子合并,再将该节点自己的(a_i)放进堆。
    合并的时候把两个堆的元素一个个弹出,将同时弹出的两个取(max)放进合并后的堆,直到某个堆弹完了为止,然后把剩下的那个堆的元素也都放进合并后的堆。
    利用启发式合并可以做到(O(nlog n))

    #include<queue>
    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using i64=long long;
    const int N=200007;
    int a[N],fa[N];char ibuf[1<<22|1],*iS=ibuf;std::priority_queue<int>q[N];
    int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    void merge(int u,int v)
    {
        std::vector<int>t;
        while(q[u].size()&&q[v].size()) t.push_back(std::max(q[u].top(),q[v].top())),q[u].pop(),q[v].pop();
        if(q[v].size()) std::swap(q[u],q[v]);
        for(int x:t) q[u].push(x);
    }
    int main()
    {
        fread(ibuf,1,1<<22,stdin);
        int n=read();i64 ans=0;
        for(int i=1;i<=n;++i) a[i]=read();
        for(int i=2;i<=n;++i) fa[i]=read();
        for(int i=n;i^1;--i) q[i].push(a[i]),merge(fa[i],i);
        while(!q[1].empty()) ans+=q[1].top(),q[1].pop();
        printf("%lld",ans+a[1]);
    }
    

    希望

    Luogu
    LOJ
    BZOJ
    如果我们固定了(k)个连通块,那么合法的中心点也会构成一个连通块。
    考虑经典充斥,对于任意一个树上连通块都有(|V|=|E|+1),因此可以先枚举一个中心点(u)计算有(P_u)个能够到达该点连通块,再枚举一条边(e)计算有(Q_e)个连通块能够到达该条边两端。最后的答案就是(sumlimits_{uin V}P_u^k-sumlimits_{ein E}Q_e^k)
    考虑dp,设(f_{u,i})表示(u)子树中包含(u)点且任意点到(u)的距离不超过(i)的连通块数,(g_{u,i})表示(u)子树外包含(u)点且任意点到(u)的距离不超过(i)的连通块数,转移是非常简单的:
    (f_{u,i}=prodlimits_{vin son_u}(f_{v,i-1}+1))
    (g_{u,i}=1+f_{fa_u,i-1}prodlimits_{vin son_{fa}wedge v e u}(f_{v,i-2}+1))
    那么对点(u)而言,(P_u=f_{u,l}g_{u,l})。对边(e=(fa_u,u))而言,(Q_e=f_{v,l-1}(g_{v,l}-1))
    考虑长链剖分优化。
    (f)从长儿子处继承dp数组,轻儿子暴力转移即可。(g)将父亲的dp数组继承给长儿子,轻儿子暴力转移即可。
    后缀乘可以通过全局乘+前缀打除法标记来实现。
    有一个问题是要求(prodlimits_{vin son_{fa}wedge v e u}(f_{v,i-2}+1)),如果我们用(frac{f_{fa_u,i-1}}{f_{u,i-2}+1})来算的话可能会出现除以(0)的情况。
    考虑枚举(fa_u)的儿子,实时维护右兄弟的积,利用求(f)时的信息计算左兄弟的积。这里需要用栈之类的结构来支持撤销。

    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using pi=std::pair<int,int>;
    const int N=1000007,P=998244353;
    char ibuf[1<<24|1],*iS=ibuf;int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
    void dec(int&a,int b){a-=b,a+=a>>31&P;}
    void mul(int&a,int b){a=1ll*a*b%P;}
    int pow(int a,int k){int r=1;for(;k;k>>=1,mul(a,a))if(k&1)mul(r,a);return r;}
    int n,l,k,ans,fa[N],h[N],son[N],dp[N],inv[N],pool[8*N],*it=pool,t0[N],t1[N];
    struct node{int a,b,inv,pos,num;}E;
    std::vector<int>e[N];std::vector<std::pair<node,std::vector<pi>>>stk[N];
    void dfs(int u)
    {
        dp[u]=1;
        for(int v:e[u]) if(v^fa[u]) if(fa[v]=u,dfs(v),mul(dp[u],dp[v]),h[v]>h[son[u]]) son[u]=v;
        inc(dp[u],1),h[u]=h[son[u]]+1,inv[u]=pow(dp[u],P-2);
    }
    namespace calf
    {
        node t[2*N];int*f[2*N];
        int get(int u,int i){return (1ll*(i<t[u].pos? f[u][i]:t[u].num)*t[u].a+t[u].b)%P;}
        void put(int u,int i,int v){mul(f[u][i]=t[u].inv,v-t[u].b+P);}
        void merge(int u,int v,int len)
        {
    	node tmp=t[u];std::vector<pi>vec;
    	for(int i=1;i<=len;put(u,i,1ll*get(v,i-1)*get(u,i)%P),++i) if(vec.emplace_back(i,f[u][i]),i==t[u].pos) f[u][t[u].pos++]=t[u].num;
    	if(len<l)
    	{
    	    int val=get(v,len);
    	    if(!val) t[u].pos=len+1,t[u].num=P-1ll*t[u].b*t[u].inv%P;
    	    else
    	    {
    		vec.emplace_back(0,f[u][0]);
    		for(int i=0;i<=len;++i) put(u,i,1ll*get(u,i)*inv[v]%P);
    		mul(t[u].a,val),mul(t[u].b,val),mul(t[u].inv,inv[v]);
    	    }
    	}
    	if(u<=n) stk[u].emplace_back(tmp,vec);
        }
        void dfs(int u)
        {
    	if(son[u]) f[son[u]]=f[u]+1,dfs(son[u]),t[u]=t[son[u]]; else t[u]=E;
    	put(u,0,1);
    	for(int v:e[u]) if(v^fa[u]&&v^son[u]) f[v]=it,it+=h[v],dfs(v),merge(u,v,std::min(h[v]-1,l));
    	t0[u]=get(u,std::min(h[u],l)-1),t1[u]=get(u,std::min(h[u]-1,l)),++t[u].b;
        }
        void undo(int u){t[u]=stk[u].back().first;for(auto[p,x]:stk[u].back().second) f[u][p]=x;stk[u].pop_back();}
        void work(){f[1]=it,it+=h[1],dfs(1);}
    }
    namespace calg
    {
        node t[N];int*g[N];
        int get(int u,int i){return(1ll*(i<t[u].pos? g[u][i]:t[u].num)*t[u].a+t[u].b)%P;}
        void put(int u,int i,int v){mul(g[u][i]=t[u].inv,v-t[u].b+P);}
        void dfs(int u)
        {
    	if(h[u]>=l+1) put(u,h[u]-l-1,1);
    	inc(ans,pow(1ll*t1[u]*get(u,h[u]-1)%P,k));
    	if(fa[u]) dec(ans,pow(1ll*t0[u]*(get(u,h[u]-1)-1+P)%P,k));
    	if(!son[u]) return ;
    	std::vector<int>ch;int mx=0,v;
    	for(int v:e[u]) if(v^fa[u]&&v^son[u]) ch.push_back(v),mx=std::max(mx,h[v]);
    	mx=std::min(mx,l),std::reverse(ch.begin(),ch.end()),calf::f[u+n]=it,it+=mx+1,calf::t[u+n]=E,calf::put(u+n,0,1);
    	for(int v:ch)
    	{
    	    calf::undo(u),g[v]=it,it+=h[v];
    	    for(int i=std::max(h[v]-l-1,0);i<h[v];++i) g[v][i]=l+i+1==h[v]? get(u,h[son[u]]-h[v]+i):1ll*get(u,h[son[u]]-h[v]+i)*calf::get(u,std::min(h[u]-1,l-h[v]+i))%P*calf::get(u+n,std::min(mx,l-h[v]+i))%P;
    	    t[v]=E,calf::merge(u+n,v,std::min(h[v]-1,l)),dfs(v);
    	}
    	v=son[u],g[v]=g[u],t[v]=t[u];
    	for(int i=std::max(h[v]-l,0);i<=h[v]+mx-l-1;put(v,i,1ll*get(v,i)*calf::get(u+n,l-h[v]+i)%P),++i) if(t[v].pos==i) g[v][t[v].pos++]=t[v].num;
    	if(mx<l)
    	{
    	    int val=1,iv=1;
    	    for(int v:ch) mul(val,dp[v]),mul(iv,inv[v]);
    	    if(!val) t[v].pos=h[v]+mx-l,t[v].num=P-1ll*t[v].b*t[v].inv%P;
    	    else
    	    {
    		for(int i=std::max(h[v]-l-1,0);i<=h[v]+mx-l-1;++i) put(v,i,1ll*get(v,i)*iv%P);
    		mul(t[v].a,val),mul(t[v].b,val),mul(t[v].inv,iv);
    	    }
    	}
    	++t[v].b,dfs(v);
        }
        void work(){t[1]=E,g[1]=it,it+=h[1],dfs(1);}
    }
    int main()
    {
        fread(ibuf,1,1<<24,stdin),n=read(),l=read(),k=read(),E={1,1,1,n,0};
        for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
        dfs(1),calf::work(),calg::work(),printf("%d",ans);
    }
    
  • 相关阅读:
    Day09 约束
    Day08 数据处理
    Day07 创建和管理表
    Day06 子查询
    Appium学习_01(连接、apppackage名查询、appactivity名查询)
    我的读书方法
    Python自动化学习记录
    FineReport帆软报表学习积累
    ERP企业资源管理 详解
    Centos7学习记录
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12733923.html
Copyright © 2011-2022 走看看