[考试反思]0430省选模拟84:乏力
这两天状态又开始下滑了?
且不提这个,更奇怪的是:最近中午我居然开始犯困了?
啥时候我还需要午休了?奇了怪了。明明晚上睡觉时间都没变。
连着三天睡午觉了,每次起来都懵里懵登的。一下午都没啥状态。
突然就莫名嗜睡。。。一天睡$6$小时以上也不是个事啊。。。
到底是题太难了学累了还是真的就是我虚了
扯远了,回到这场考试:
$T1$细节连篇,$T2$思路神仙,$T3$码量飞天。
$T1$是那种思路不复杂但是需要大规模分类讨论丢一个就大爆炸的题。
然后少讨论了一种情况直接炸掉$60$分。尝试写对拍然而不是很会造数据。
反正又是和数据结构和分类讨论和各种细节抗争了半天,用了不少时间结果最后也没多少分。
$T2$的话觉得好像是大神题于是分配的时间不多。
会写$60$的部分分然而觉得会非常难写于是弃了打$30$就跑路了。
$T3$写了个弱智哈希应该有$30$来着,然后哈希表居然写挂了。就爆零了。
到底是题太难了学累了还是真的就是我虚了
无力吐槽。
T1:挖宝
大意:树,$q$次询问给定$a,b,d_a,d_b$求树上一点满足距离$a$为$d_a$且距离$b$为$d_b$。$n,q \le 10^6,Time\ Limit:5s$
显然答案与$a,b$之间的路径有很大关系。
发现如果$d_a+d_b < dis(a,b)$显然无解。$d_a+d_b -dis(a,b) \equiv 1 (\mod 2)$也无解。
问题转化为我们知道路径$(a,b)$上一点$c$,求一个点$x$距离满足$c$恰好为$E=\frac{d_a+d_b-dis(a,b)}{2}$且满足$(x,c)$与$(a,b)$只有一个交点$c$
其中$c$点在路径上与$a$相距$d_a-E$。
倍增维护一下$2^i$辈祖先与$2^i$辈后代,因为可能需要剔除两棵子树所以需要维护第$1,2,3$深的子树是哪个。
比较容易忽视的一种情况是$c=lca(a,b)$时,$c$可以朝父亲方向走,且是朝父亲方向走几步(不一定到根)之后转弯走向别的子树。
可以再换根$dp$求出一个$tp[]$数组表示从当前点出发走到祖先链上哪个点之后折到另一个子树里所走的距离最大。
然后就只需要一顿暴写一顿暴调就可以了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 1000005 4 int f[20][S],fir[S],l[S<<1],to[S<<1],ec,c[20][S],c2[S],c3[S],dep[S],mxd[S],n,q,tp[S]; 5 int read(){int p=0;char ch=getchar();for(;!isdigit(ch);ch=getchar());for(;isdigit(ch);ch=getchar())p=p*10+ch-48;return p;} 6 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 7 void dfs(int p,int fa){ 8 dep[p]=mxd[p]=dep[fa]+1; f[0][p]=fa; 9 for(int i=1;i<20;++i)f[i][p]=f[i-1][f[i-1][p]]; 10 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 11 dfs(to[i],p); mxd[p]=max(mxd[p],mxd[to[i]]); 12 if(mxd[to[i]]>mxd[c[0][p]])c3[p]=c2[p],c2[p]=c[0][p],c[0][p]=to[i]; 13 else if(mxd[to[i]]>mxd[c2[p]])c3[p]=c2[p],c2[p]=to[i]; 14 else if(mxd[to[i]]>mxd[c3[p]])c3[p]=to[i]; 15 }for(int i=1;i<20;++i)c[i][p]=c[i-1][c[i-1][p]]; 16 } 17 void DFS(int p,int fa,int Tp,int D){ 18 D++;tp[p]=Tp; 19 if(c[0][p])if(D>=mxd[c2[p]]-dep[p])DFS(c[0][p],p,Tp,D);else DFS(c[0][p],p,p,mxd[c2[p]]-dep[p]); 20 for(int i=fir[p];i;i=l[i])if(to[i]!=fa&&to[i]!=c[0][p]) 21 if(D>=mxd[p]-dep[p])DFS(to[i],p,Tp,D);else DFS(to[i],p,p,mxd[p]-dep[p]); 22 } 23 int getfa(int p,int d){for(int i=19;~i;--i)if(d&1<<i)p=f[i][p];return p;} 24 int lca(int a,int b){ 25 b=getfa(b,dep[b]-dep[a]); 26 if(a==b)return a; 27 for(int i=19;~i;--i)if(f[i][a]!=f[i][b])a=f[i][a],b=f[i][b]; 28 return f[0][a]; 29 } 30 int getson(int p,int d,int x,int y){ 31 if(x==c[0][p]&&y==c2[p])p=c3[p],d--; 32 else if(y==c[0][p]&&x==c2[p])p=c3[p],d--; 33 else if(x==c[0][p]||y==c[0][p])p=c2[p],d--; 34 for(int i=19;~i;--i)if(d&1<<i)p=c[i][p]; 35 return p?p:-1; 36 } 37 int getans(int p,int d,int x,int y){ 38 if(!d)return p; 39 if(x){ 40 int r=getson(p,d,x,y);if(r!=-1)return r; 41 return dep[p]-dep[tp[p]]>=d?getfa(p,d):getson(tp[p],d-dep[p]+dep[tp[p]],0,getfa(p,dep[p]-dep[tp[p]]-1)); 42 }return getson(p,d,y,0); 43 } 44 int main(){ 45 n=read();q=read(); 46 for(int i=1,a,b;i<n;++i)a=read(),b=read(),link(a,b),link(b,a); 47 dfs(1,0);DFS(1,0,0,-1); 48 while(q--){ 49 int a,b,da,db,LCA,c,E,op,dis; 50 a=read();da=read();b=read();db=read(); 51 if(dep[a]>dep[b])swap(a,b),swap(da,db); 52 LCA=lca(a,b); dis=dep[a]+dep[b]-2*dep[LCA]; 53 if(da+db-dis&1){puts("-1");continue;} 54 if(da+db<dis){puts("-1");continue;} 55 E=da+db-dis>>1; 56 c=(op=dep[a]-dep[LCA]>=da-E)?getfa(a,da-E):getfa(b,db-E); 57 if(c==LCA)printf("%d\n",getans(c,E,getfa(b,dep[b]-dep[c]-1),a==c?0:getfa(a,dep[a]-dep[c]-1))); 58 else if(op)printf("%d\n",getans(c,E,0,getfa(a,da-E-1))); 59 else printf("%d\n",getans(c,E,0,getfa(b,db-E-1))); 60 } 61 }
T2:图
大意:给定无向图,求有多少导出子图是联通的。答案对$2$取模。$n \le 50$。边两个端点的编号差$\le 12$
首先这题非常奇怪,模数非常的可爱(?)且最后一个限制条件也很特殊。
最后这个限制条件给人的第一反应就是《奇怪的道路》那种记录最后几位的状压方式。
然而这题该压什么呢?怎么结合两部分的特性?
我们考虑,如果一个导出子图的联通块数是$\ge 2$的那么它就不应该作出贡献,如果是$1$就应该作出$1$贡献。
然而再模$2$意义下,作出偶数贡献相当于没作出贡献。所以我们可以设$t$为联通块数的话,对每个联通块使之作出$2^{t-1}$的贡献即可。
$-1$不好办。那就去掉它,计算时对$4$取模最后输出时除$2$就行。
那么怎么让每个联通块作出$2$贡献?二色染色。
状压最近的$12$个点表示:被染了什么颜色或者没被染色(不出现在导出子图里)
要求每个联通块内部所有点颜色相同,那么贡献的方案数就是$2^t$了。问题得以解决。$O(3^{12}n)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,dp[2][1<<24],ban[55],tot; 4 vector<int>v[55],S; 5 void sch(int al,int s){ 6 if(al==12)return S.push_back(s); 7 for(int i=0;i<3;++i)sch(al+1,s<<2|i); 8 } 9 int main(){ 10 cin>>n>>m; 11 for(int i=1,a,b;i<=m;++i){ 12 scanf("%d%d",&a,&b); 13 if(a>b)swap(a,b); a=b-a-1; 14 ban[b]|=1<<a<<a; 15 } 16 sch(0,0); const int M=(1<<24)-1; 17 int nw=1,nx=0; dp[0][0]=1; 18 for(int i=1;i<=n;++i){ 19 nw^=1;nx^=1; 20 for(int j=0;j<S.size();++j){ 21 int s=S[j]; 22 (dp[nx][s<<2&M]+=dp[nw][s])&=3; 23 if(!(ban[i]<<1&s))(dp[nx][(s<<2|1)&M]+=dp[nw][s])&=3; 24 if(!(ban[i]&s))(dp[nx][(s<<2|2)&M]+=dp[nw][s])&=3; 25 dp[nw][s]=0; 26 } 27 }for(int j=0;j<S.size();++j)tot+=dp[nx][S[j]]; 28 cout<<tot%4/2<<endl; 29 }
T3:字符串
大意:对于给定字符串,对于其每个长为$m$的子串计算:有多少个长为$m$的子串与该串相差$\le 1$个字符。$n,m \le 10^5$
问题等价于,对于每个$i$求出有多少个$j$满足$lcp(suffix(i-m+1),suffix(j-m+1))+lcs(prefix(i),prefix(j)) \ge m-1$
总算转换成了前缀后缀$lcp,lcs$之类的常见问题。二话不说正反建$SAM$弄出$fail$树。
考虑问题在$fail$树上的形式:对于每个$i$求出有多少$j$满足$len_{prefix}(lca_{prefix}(i,j))+len_{suffix}(lca_{suffix}(i,j)) \ge m-1$
我们先$dfs$一棵树。发现要处理的是所有点对之间的关系,可能的技巧是点分治或者树上启发式合并。
前者不想写不会用,考虑后者。假如已经在$dsu$里的点都已经维护好了现在我们要新加入一个点$x$,考虑这个点与原点集之间的关系:
加入当前在第一棵树的节点$p$,那么也就是说我想知道有多少个点集中的点$i$满足$len(lca_2(i,x)) \ge m-1-len(p)$
问题也就是说:问有多少个点和$x$的$lca$的深度(指对应的字符串长度)大于等于某个特定值。
也就是说在问:点集中有多少个点在另一棵树里的$x$的祖先链上深度为$m-1-len(p)$的那个点 的子树里。
这个可以直接$dfs$序+树状数组维护。单点加区间查询。这样就可以更新$x$的答案。
然而没有完,在插入$x$的同时在另一棵树的那个子树里的所有点的答案也要$+1$。
再开一个树状数组做区间加单点查询,每个点插入和删除的时候分别查询一下做差就可以了。
有一些细节。。。时间复杂度是$O(nlog^2n)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 200005 4 char s[S];int n,fir[S],l[S],to[S],ec,m,dfn[S],dfr[S],d[S],f[17][S],tim,ans[S]; 5 int dep[S],sz[S],hson[S],tag[S]; vector<int>v[S]; 6 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 7 struct SAM{ 8 int c[26][S],f[S],len[S],pc,lst,re[S],ord[S]; 9 void extend(int C){ 10 int p=lst,np=lst=++pc,q,nq; 11 len[np]=re[np]=len[p]+1; ord[len[np]]=np; 12 for(;p&&!c[C][p];p=f[p])c[C][p]=np; 13 if(!p){f[np]=1;return;} 14 if(len[q=c[C][p]]==len[p]+1)f[np]=q; 15 else{ 16 nq=++pc; len[nq]=len[p]+1; 17 for(int i=0;i<26;++i)c[i][nq]=c[i][q]; 18 f[nq]=f[q]; f[np]=f[q]=nq; 19 for(;c[C][p]==q;p=f[p])c[C][p]=nq; 20 } 21 } 22 void build(){ 23 ec=0;for(int i=1;i<=pc;++i)fir[i]=0; 24 for(int i=2;i<=pc;++i)link(f[i],i); 25 } 26 }pre,suf; 27 struct FT{ 28 int t[S]; 29 void add(int p,int v){for(;p<S;p+=p&-p)t[p]+=v;} 30 int ask(int p,int a=0){for(;p;p^=p&-p)a+=t[p];return a;} 31 }T1,T2; 32 void dfs(int p){ 33 d[p]=pre.len[p]; dfn[p]=++tim; for(int i=1;i<17;++i)f[i][p]=f[i-1][f[i-1][p]]; 34 for(int i=fir[p];i;i=l[i])f[0][to[i]]=p,dfs(to[i]); 35 dfr[p]=tim; 36 } 37 void DFS(int p){ 38 sz[p]=suf.re[p]?1:0; dep[p]=suf.len[p]; 39 for(int i=fir[p];i;i=l[i]){ 40 DFS(to[i]);sz[p]+=sz[to[i]]; 41 if(sz[to[i]]>sz[hson[p]])hson[p]=to[i]; 42 } 43 } 44 int anc(int p,int D){D=max(D,0);for(int i=16;~i;--i)if(d[f[i][p]]>=D)p=f[i][p]; return p;} 45 #define N(p) suf.re[p] 46 #define X(p) pre.ord[n+m-N(p)] 47 void Dfs(int p,int op){ 48 for(int i=fir[p];i;i=l[i])if(to[i]!=hson[p])Dfs(to[i],0); 49 if(hson[p])Dfs(hson[p],1),swap(v[p],v[hson[p]]); 50 if(N(p)>=m){ 51 int r=tag[p]=anc(X(p),m-1-dep[p]); 52 ans[N(p)]+=T1.ask(dfr[r])-T1.ask(dfn[r]-1),T2.add(dfn[r],1),T2.add(dfr[r]+1,-1); 53 T1.add(dfn[X(p)],1),ans[N(p)]-=T2.ask(dfn[X(p)]),v[p].push_back(p); 54 } 55 for(int i=fir[p],y;y=to[i];i=l[i])if(y!=hson[p]){ 56 for(int j=0,z,r;z=j<v[y].size()?v[y][j]:0;++j)r=tag[z]=anc(X(z),m-1-dep[p]),ans[N(z)]+=T1.ask(dfr[r])-T1.ask(dfn[r]-1),T2.add(dfn[r],1),T2.add(dfr[r]+1,-1); 57 for(int j=0,z;z=j<v[y].size()?v[y][j]:0;++j)T1.add(dfn[X(z)],1),ans[N(z)]-=T2.ask(dfn[X(z)]),v[p].push_back(z); 58 } 59 if(!op){ 60 for(int j=0,z;z=j<v[p].size()?v[p][j]:0;++j)T1.add(dfn[X(z)],-1),ans[N(z)]+=T2.ask(dfn[X(z)]); 61 for(int j=0,z,r;z=j<v[p].size()?v[p][j]:0;++j)r=tag[z],T2.add(dfn[r],-1),T2.add(dfr[r]+1,1); 62 } 63 } 64 int main(){ 65 scanf("%d%d%s",&n,&m,s+1); d[0]=-1; 66 pre.lst=pre.pc=suf.lst=suf.pc=1; 67 for(int i=n;i;--i)pre.extend(s[i]-'a'); 68 pre.build(); dfs(1); 69 for(int i=1;i<=n;++i)suf.extend(s[i]-'a'); 70 suf.build(); DFS(1); Dfs(1,0); 71 for(int i=m;i<=n;++i)printf("%d ",ans[i]); 72 }