CF/AT 乱做

Posted cnyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF/AT 乱做相关的知识,希望对你有一定的参考价值。

CF504E Misha and LCP on Tree [*3000]

原题链接

Solution

垃圾题。
考虑 LCP 问题的经典解法,二分一个前缀,判定这个前缀能否成为 LCP。
判定前缀考虑 Hash,需要预处理出每个点从根到它,从它到根的哈希值。
然后我们发现,每次二分之后需要找终点,这个得 \\(O(1)\\) 做,使用长链剖分求 k 级祖先就可以了。
然后你就判判判,然后你就被迫要卡常,总之就很离谱。
时间复杂度 \\(O(m\\log n)\\),其中 \\(\\log n\\) 是倍增 LCA。
然后用 ST 表 LCA 可以做到 \\(O(n\\log n+m)\\),不过我没写。

Code
/*
Author: cnyz
----------------
Looking! The blitz loop this planet to search way.
Only my RAILGUN can shoot it. 今すぐ
*/
#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include<bits/stdc++.h>
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned int uint;
#define pb push_back
#define mp make_pair
#define fi first
#define se second

template <typename T> void chkmax(T &x,T y) {
    if(x<y) x=y;
}
template <typename T> void chkmin(T &x,T y) {
    if(x>y) x=y;
}

const int MAXL=1<<22;
char i_str[MAXL],o_str[MAXL],*i_s,*i_t;
int o_t;
inline char gc() {
    if(i_s==i_t) {
        i_s=i_str;
        i_t=i_s+fread(i_str,1,MAXL,stdin);
        return i_s==i_t?EOF:*i_s++;
    } else return *i_s++;
}
inline void gs(char *s) {
    *s=gc();
    while(*s==\' \'||*s==\'\\n\')*s=gc();
    while(*s!=\' \'&&*s!=\'\\n\')*++s=gc();
    *s=\'\\0\';
}
inline int read() {
    int x=0,f=0;
    char ch=gc();
    for(; ch<\'0\'||ch>\'9\'; ch=gc())
        if(ch==\'-\')f=1;
    for(; ch>=\'0\'&&ch<=\'9\'; ch=gc())
        x=(x<<1)+(x<<3)+(ch^48);
    return f?~x+1:x;
}
#define fl fwrite(o_str,1,o_t,stdout),o_t=0
inline void pc(char x) {
    o_str[o_t++]=x;
    if(o_t==MAXL)fl;
}
inline void write(ll x) {
    if(x<0)x=-x,pc(\'-\');
    if(x>9)write(x/10);
    pc(x%10^48);
}

const int mod=998244353;
const int base=13331;
const int N=6e5;
int Up[N+10],Down[N+10];
int n,q,h[N+10],ju[N+10][20];
vector<int> G[N+10];
int md[N+10],dep[N+10],hson[N+10],top[N+10];
int po[N+10],inv[N+10];
int pos[N+10],dfn[N+10],jp[N+10],dc;
char ch[N+10];
int fpow(int x,int y) {
    int ret=1;
    for(;y;y>>=1) {
        if(y&1) ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
    }
    return ret;
}
void ad(int u) {
    Down[u]=(1ll*Down[ju[u][0]]*base+ch[u])%mod;
    Up[u]=(Up[ju[u][0]]+1ll*po[dep[ju[u][0]]]*ch[u])%mod;
    dep[u]=dep[ju[u][0]]+1;
    for(int i=1;i<=19;i++) ju[u][i]=ju[ju[u][i-1]][i-1];
    for(auto v:G[u]) {
        if(v==ju[u][0]) continue;
        ju[v][0]=u;ad(v);
        if(md[v]+1>md[u]) md[u]=md[hson[u]=v]+1;
    }
}
void dfs(int u) {
    if(hson[ju[u][0]]==u) top[u]=top[ju[u][0]];
    else top[u]=u;
    pos[dfn[u]=++dc]=u;
    if(hson[u]) dfs(hson[u]);
    for(auto v:G[u]) {
        if(v==ju[u][0]||v==hson[u]) continue;
        dfs(v);
        jp[dfn[v]]=u;
        for(int i=1;i<=md[v];i++)
            jp[dfn[v]+i]=ju[jp[dfn[v]+i-1]][0];
    }
}
int LCA(int u,int v) {
    if(u==v) return u;
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=19;i>=0;i--)
        if(dep[ju[u][i]]>=dep[v])
            u=ju[u][i];
    if(u==v) return u;
    for(int i=19;i>=0;i--)
        if(ju[u][i]!=ju[v][i])
            u=ju[u][i],v=ju[v][i];
    return ju[u][0];
}
int calc(int u,int k) {
    return jp[dfn[u]+k-1];
}
int ask(int u,int k) {
    if(!k) return u;
    u=ju[u][h[k]];
    k-=1<<h[k];
    if(dep[u]-dep[top[u]]<k)
        return calc(top[u],-dep[u]+k+dep[top[u]]);
    else return pos[dfn[u]-k];
}
void init() {
    ad(1);
    // up[1].pb(1);
    // for(int i=1;i<=md[1];i++) up[1].pb(ju[up[1].back()][0]);
    dfs(1);
}
int get(int a,int b,int p) {
    if(p==-1) p=b;
    int H1=1ll*(Up[a]-Up[ju[p][0]]+mod)*inv[dep[p]-1]%mod;
    int H2=(Down[b]-1ll*Down[p]*po[dep[b]-dep[p]]%mod+mod)%mod;
    return (1ll*H1*po[dep[b]-dep[p]]+H2)%mod;
}
int query(int a,int b,int c,int d) {
    int lca1=LCA(a,b),lca2=LCA(c,d);
    int dis1=dep[a]+dep[b]-2*dep[lca1]+1,
        dis2=dep[c]+dep[d]-2*dep[lca2]+1;
    int l=1,r=min(dis1,dis2);
    while(l<=r) {
        int mid=(l+r)>>1;
        // printf("[%d,%d] is %d\\n",l,r,mid);
        int h1=0,h2=0;
        if(dep[a]-dep[lca1]+1>=mid) h1=get(a,ask(a,mid-1),-1);
        else h1=get(a,ask(b,dis1-mid),lca1);
        if(dep[c]-dep[lca2]+1>=mid) h2=get(c,ask(c,mid-1),-1);
        else h2=get(c,ask(d,dis2-mid),lca2);
        // printf("%d %d\\n",h1,h2);
        if(h1==h2) l=mid+1;
        else r=mid-1;
    }
    return l-1;
}
int main() {
    n=read();
    gs(ch+1);
    po[0]=inv[0]=1;
    po[1]=base,inv[1]=fpow(base,mod-2);
    for(int i=2,x,y;i<=n;i++) {
        x=read(),y=read();
        G[x].pb(y),G[y].pb(x);
        po[i]=1ll*po[i-1]*base%mod,
        h[i]=h[i/2]+1,
        inv[i]=1ll*inv[i-1]*inv[1]%mod;
    }
    init();
    q=read();
    while(q--) {
        int a,b,c,d;
        a=read(),b=read(),c=read(),d=read();
        write(query(a,b,c,d)),pc(\'\\n\');
    }
    return fl;
}

杂题乱做4

P8499

首先,显然需要树哈希。哈希方法见 OIwiki。

\\(f_i\\) 表示 \\(i\\) 子树的哈希值,那么我们如何判断 \\(G\\) 能否通过删去不超过 \\(k\\) 个点变成 \\(H\\)

考虑 \\(solve(i,j,delta)\\) 表示我们需要判断 \\(G\\)\\(i\\) 子树是否能通过删去不超过 \\(delta\\) 个点变成 \\(H\\)\\(j\\) 子树。

那么我们执行 \\(solve(i,j,delta)\\) 时,考虑枚举 \\(i,j\\) 的每一个儿子,并将这些儿子中子树形态相同的能匹配就匹配上,然后扔掉(可以通过预处理,对儿子的子树哈希值排序,然后直接双指针)。

可以发现这样贪心扔掉尽可能多的对一定会是最优解之一。证明显然。

然后考虑剩下的不同对。

直接对 \\(i\\) 的未匹配儿子枚举全排列,挨个判断一下按这个排列能否匹配 \\(j\\) 的未匹配儿子即可。

加一些剪枝优化即可通过。

具体地,当 \\(i\\) 的儿子个数小于 \\(j\\) 的儿子个数,枚举全排列时如果任意一对匹配满足 \\(size_a_i < size_b_j\\) 就直接不考虑这个序列,计算 \\(\\sum size_a_i-size_b_j\\) 如果中途大于 \\(delta\\) 了就直接不合法。

复杂度比较玄学,但是容易发现这个算法的复杂度接近 \\(O(n\\times k!)\\)

P8290

考虑枚举最小值 \\(x\\),设 \\(f_u,x,0/1\\) 表示当前考虑 \\(u\\) 到某个子树内的一条链,最小值钦定是 \\(x\\),是否已经有最小值出现的方案数,再设 \\(g_u,x,0/1\\) 表示和,那么转移是容易的。

然后对于枚举一棵新子树的时候,我们在转移的同时也要考虑合并 \\(u\\) 到前面子树和 \\(u\\) 到这棵子树的链的并。这个也是简单的。

发现直接这样算,复杂度 \\(O(nV)\\)

但是可以发现,如果 \\(x\\) 不是 \\(l_i,r_i,l_i-k,r_i-k\\) 类似的这些值,那么 \\(x\\) 在变化的过程中,答案值实际上是一个多项式。

于是对于这 \\(O(n)\\) 段选 \\(n\\) 个数出来 \\(dp\\) 然后直接插值即可。

以上是关于CF/AT 乱做的主要内容,如果未能解决你的问题,请参考以下文章

杂题乱做4

Atcoder 乱做

codeforces 乱做题记录

Web公路,新手上路!NO.3 [ 乱做一通的基本视频网页]

python 装饰器

微信小程序代码片段