【比赛链接】http://59.61.75.5:8018/contest/223
A. 欧拉路径树
【题解】
考虑 dfs 依次处理每个子树.
显然对于每一种数字,相邻两个同种数字间的即为该节点的一个子树. 递归处理.
现在考虑一种情况:$1 2 dots 2 -1 -1 1$. 假设 $3$ 还没出现过,则有两种填法:$1 2 dots 2 1 3 1$ 和 $1 2 dots 2 3 2 1$.
注意到这两种填法分别对应的树的形态为 $3$ 为 $2$ 子节点及 $3$ 为 $1$ 子节点. 画图分析知,这两种情况对其它任意节点没有区别.
因此我们考虑贪心,尽可能地多填当前的 $1$. 即使每棵子树尽量小,子树个数尽量多.
显然先递归处理 $2 dots 2$ 这棵子树,递归回来后可压缩成单个节点 $2$.
现在考虑处理 $1 2 -1 -1 1$ 类型的问题. 根据树的性质,应该用一个栈来维护.
若碰到一个非 $-1$ 的数,且当前数出现过(可能是 $-1$ 造成的),则判断与栈顶的下一个数是否相等,相等即可将栈顶弹出,否则入栈.
对于 $-1$,存在一种情况使得必须填后面出现过的数:$1 -1 3 2 1$. 这时候必须填 $2$. 分析可得:当 $2(sum_{j-1}-sum_{i})=j-i$ 且 $i,j$ 奇偶性相同时,可以填入 $a[j]$.
上述式子可转化为 $2 sum_{i}-i=2sum{j-1}-j$. 用数据结构(splay 套 splay map 套 set)分奇偶性维护即可.
因此对于每个 $-1$ 有:若能找到能填的位置就填入,并压栈;否则弹栈即可. 若栈空则选一个未使用过的数填入.
效率 $O(n log n)$ 或 $O(n log^2 n)$(根据数据结构的使用而定,可能存在线性做法). 期望得分:100.
【代码】
1 #include<bits/stdc++.h> 2 inline int read ( void ) 3 { 4 int x=0;char ch;bool f=true; 5 while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false; 6 for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); 7 return f ? x : -x ; 8 } 9 const int maxn=1000000+10; 10 int a[maxn],st[maxn],tp,n,sum[maxn],vis[maxn],times; 11 std::map<int,std::set<int>> s[2]; 12 std::queue<int> unused; 13 std::vector<int> pos[maxn]; 14 inline void solve ( int l,int r,int x ) 15 { 16 std::vector<int> G; 17 for ( int i=l;i<=r;i++ ) 18 if ( ~a[i] ) 19 { 20 for ( int j=0;j<(int)pos[a[i]].size()-1;j++ ) solve(pos[a[i]][j]+1,pos[a[i]][j+1]-1,a[i]); 21 G.push_back(a[i]);i=pos[a[i]].back(); 22 } 23 else G.push_back(-i); 24 st[tp=1]=x;sum[0]=(G[0]>0);s[0].clear();s[1].clear();++times; 25 for ( int i=1;i<(int)G.size();i++ ) 26 if ( G[i]>0 ) sum[i]=sum[i-1]+1,s[i&1][i-2*sum[i-1]].insert(i); 27 else sum[i]=sum[i-1]; 28 for ( int i=0;i<(int)G.size();i++ ) 29 if ( G[i]>0 ) 30 { 31 s[i&1][i-2*sum[i-1]].erase(i); 32 if ( tp>1 and st[tp-1]==G[i] ) tp--; 33 else st[++tp]=G[i]; 34 } 35 else 36 { 37 if ( !s[i&1][i-2*sum[i]].empty() ) 38 { 39 int j=*s[i&1][i-2*sum[i]].begin(); 40 a[-G[i]]=G[j]; 41 if ( vis[G[j]]==times ) while ( st[tp]!=G[j] ) tp--; 42 else vis[G[j]]=times,st[++tp]=G[j]; 43 } 44 else 45 { 46 if ( tp>1 ) a[-G[i]]=st[--tp]; 47 else st[++tp]=a[-G[i]]=unused.front(),unused.pop(); 48 } 49 } 50 } 51 signed main() 52 { 53 for ( int T=read();T--; ) 54 { 55 n=read(); 56 for ( int i=1;i<(n<<1);i++ ) a[i]=read(); 57 a[1]=a[2*n-1]=1; 58 for ( int i=1;i<(n<<1);i++ ) if ( ~a[i] ) pos[a[i]].push_back(i); 59 for ( int i=1;i<=n;i++ ) if ( pos[i].empty() ) unused.push(i); 60 for ( int i=0;i<(int)pos[1].size()-1;i++ ) solve(pos[1][i]+1,pos[1][i+1]-1,1); 61 for ( int i=1;i<(n<<1);i++ ) printf("%d%c",a[i]," "[i==2*n-1]); 62 for ( int i=1;i<=n;i++ ) pos[i].clear(); 63 } 64 return 0; 65 }
B. 匹配
【题解】
显然在 AC 自动机上用矩阵快速幂转移 dp. 记 $t=sum{|t_i|}$,$Sigma$ 为字符集,时间复杂度为 $O(nlog n+nt|Sigma|+nt^3log n+Qt^2log n)$.
考虑优化倍增预处理部分:跳 lca 时,设当前在 $x$,先跳 $2^{operatorname{lowbit}(operatorname{dep}_x)}$ 至 $y$,再跳 $2^{operatorname{lowbit}(operatorname{dep}_y)}cdots$,这样只用处理 $iin[0,operatorname{lowbit}(operatorname{dep}_x)]$ 的 $2^i$ 矩阵.
但这样还是容易被卡(如构造很多点深度都是 $1024$ 的树),所以可以考虑在根节点上加若干点来避免这种问题. 从最低点开始考虑,如果深度为偶数的点多于奇数,可以在根上加一个点,从而保证深度为奇数的点不少于深度为偶数的,同理去考虑其它二进制位. 可知 $O(t^3)$ 次乘法的次数是 $frac{n}{2} imes 1+frac{n}{4} imes 2+cdots<2n=O(n)$. 所以总时间复杂度降到了 $O(nlog n + nt|Sigma|+nt^3+Qt^2log n)$.
【代码】
1 #include<bits/stdc++.h> 2 const int mod=998244353; 3 int size,ch[50][30],fail[50],Log[5000],Low[5000],cnt[5000]; 4 int anc[5000][20],n,m,Q,dep[5000],h[5000],e_cnt,root=1,k,add; 5 bool flag[50]; 6 struct edge { int v,nxt,w; } e[10000]; 7 struct matrix 8 { 9 int a[50][50]; 10 inline int * operator [] ( const int x ) { return a[x]; } 11 matrix(){memset(a,0,sizeof(a));} 12 inline friend matrix operator * ( matrix A,matrix B ) 13 { 14 matrix C; 15 for ( int i=0;i<=size;i++ ) for ( int k=0;k<=size;k++ ) for ( int j=0;j<=size;j++ ) C[i][j]=(C[i][j]+1LL*A[i][k]*B[k][j])%mod; 16 return C; 17 } 18 }; 19 std::vector<matrix> U[3000],D[3000]; 20 inline matrix make_matrix ( int trans ) 21 { 22 std::vector<int> tr;matrix T; 23 for ( int i=1;i<=26;i++ ) if ( trans&(1<<i) ) tr.push_back(i); 24 for ( int i=0;i<=size;i++ ) for ( int j:tr ) T[i][ch[i][j]]++; 25 return T; 26 } 27 inline void dfs ( int u,int fr,int trans ) 28 { 29 dep[u]=dep[anc[u][0]=fr]+1; 30 U[u].push_back(make_matrix(trans)); 31 D[u].push_back(make_matrix(trans)); 32 for ( int i=1;anc[u][i-1];i++ ) anc[u][i]=anc[anc[u][i-1]][i-1]; 33 for ( int i=1;i<=Low[dep[u]];i++ ) 34 U[u].push_back(U[u][i-1]*U[anc[u][i-1]][i-1]), 35 D[u].push_back(D[anc[u][i-1]][i-1]*D[u][i-1]); 36 for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) dfs(e[i].v,u,e[i].w); 37 } 38 inline int lca ( int u,int v ) 39 { 40 if ( dep[u]<dep[v] ) std::swap(u,v); 41 for ( int i=Log[dep[u]];~i;i-- ) if ( dep[anc[u][i]]>=dep[v] ) u=anc[u][i]; 42 if ( u==v ) return u; 43 for ( int i=Log[dep[u]];~i;i-- ) if ( anc[u][i]!=anc[v][i] ) u=anc[u][i],v=anc[v][i]; 44 return anc[u][0]; 45 } 46 inline void addedge ( int u,int v,int w ) 47 { 48 e[++e_cnt].nxt=h[u];e[h[u]=e_cnt].v=v;e[e_cnt].w=w; 49 e[++e_cnt].nxt=h[v];e[h[v]=e_cnt].v=u;e[e_cnt].w=w; 50 } 51 signed main() 52 { 53 scanf("%d%d%d",&n,&m,&Q); 54 for ( int i=2;i<=n;i++ ) 55 { 56 int u,v,val=0;char s[30]={'