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 乱做的主要内容,如果未能解决你的问题,请参考以下文章