[考试反思]0122省选模拟12:延迟
博客咕过了一年我也就忘了我考试状态了2333。
T1是弱智题但是没想。。。写个暴力跑路了(时间不够,主要投在T2/3上了)
然而其实想到了一个乱搞,觉得能得分的概率不大,结果数据奇水完全不对的玩意还有20分,然而我并没有写。。。
然而T2写的是正解,虽说没有其他人的状压优秀,T了一个细节WA了一个拿了80分,凑合吧。
然后T3时间不多的时候写的,拿个暴力,想到了正解大概怎么写但是没有写,太恶心。
考后改题写了写,一下午就过去了。。。一晚上也就过去了。。。弄得跟我颓废了半天一样。。。
然而是真xx难写啊越写越自闭。
考试状态还行。考后改题心态崩了(?)
T1:Colorado Potato Beetle
大意:一个人走,给定路径求它包住的面积。步长$10^6$,步数$n \le 1000$
离散化+BFS。考场上想的太复杂了。白送分不要。。。
按照直觉写,写出来就差不多了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,d[1111],x[11111111],y[11111111],xc,yc,nt[6666][6666],qx[66666666],qy[66666666]; 4 char s[1111][4];long long ans; 5 map<int,int>X,Y; 6 int main(){ 7 cin>>n;int nx=0,ny=0,tx,ty,fx,fy; 8 X[0];X[1];X[-1];Y[1];Y[0];Y[-1]; 9 for(int i=1;i<=n;++i){ 10 scanf("%s%d",s[i],&d[i]); 11 if(s[i][0]=='U')nx-=d[i]; 12 if(s[i][0]=='D')nx+=d[i]; 13 if(s[i][0]=='L')ny-=d[i]; 14 if(s[i][0]=='R')ny+=d[i]; 15 X[nx];X[nx-1];X[nx+1]; 16 Y[ny];Y[ny-1];Y[ny+1]; 17 } 18 for(auto I:X)x[++xc]=I.first; 19 for(auto I:Y)y[++yc]=I.first; 20 for(int i=1;i<=xc;++i)X[x[i]]=i; 21 for(int i=1;i<=yc;++i)Y[y[i]]=i; 22 x[++xc]=y[++yc]=1000000007; 23 nx=0,ny=0; 24 for(int i=1;i<=n;++i){ 25 fx=X[nx];fy=Y[ny]; 26 if(s[i][0]=='U')nx-=d[i]; 27 if(s[i][0]=='D')nx+=d[i]; 28 if(s[i][0]=='L')ny-=d[i]; 29 if(s[i][0]=='R')ny+=d[i]; 30 tx=X[nx];ty=Y[ny]; 31 if(fx>tx||fy>ty)swap(fx,tx),swap(fy,ty); 32 for(int i=fx;i<=tx;++i)for(int j=fy;j<=ty;++j)nt[i][j]=1; 33 } 34 qx[1]=qy[1]=1;ans=(0ll+x[xc]-x[1])*(y[yc]-y[1]); 35 for(int i=1;i<=xc;++i)nt[i][0]=nt[i][yc]=1; 36 for(int i=1;i<=yc;++i)nt[0][i]=nt[xc][i]=1; 37 for(int h=1,t=1;h<=t;++h){ 38 int xx=qx[h],yy=qy[h]; 39 if(nt[xx][yy])continue; 40 nt[xx][yy]=1;ans-=(0ll+x[xx+1]-x[xx])*(y[yy+1]-y[yy]); 41 qx[++t]=xx+1;qy[t]=yy; 42 qx[++t]=xx-1;qy[t]=yy; 43 qx[++t]=xx;qy[t]=yy+1; 44 qx[++t]=xx;qy[t]=yy-1; 45 }cout<<ans<<endl; 46 }
T2:Distinct Paths
大意:$n \times m$矩阵上填$k$种颜色,有些格子已经填好,要求任意一条长度为$n+m-1$的$(1,1)$到$(n,m)$的序列上所有颜色都不能相同求方案数。$n,m \le 1000,k \le 10$
数据范围唬人,然而显然如果$n+m-1 > k$答案一定是0。所以就没那么多想法。
搜索,记搜,压状态,11进制的,每一位表示这种颜色出现过的最右位置是第几列,根据这个状态就能确定这一位能不能填某种颜色。
加个剪枝卡卡常,然后开心搜就行。
1 #include<bits/stdc++.h> 2 using namespace std; 3 unordered_map<long long,int>M[12][12]; 4 #define mod 1000000007 5 int mo(int x){return x>=mod?x-mod:x;} 6 long long chg(long long st,int p,int v){p--;return st^(st&15ll<<(p<<2))^(1ll*v<<(p<<2));} 7 int n,m,k,lp[12][12][12],ac[12][12],ok[12][12][12]; 8 int sch(int x,int y,long long st){ 9 if(x==n&&y==m+1)return 1; 10 if(y==m+1)x++,y=1; 11 if(M[x][y][st])return M[x][y][st]; 12 long long rs=st; 13 for(int i=1;i<=k;++i)lp[x][y][i]=rs&15,rs>>=4; 14 if(ac[x][y])return M[x][y][st]=lp[x][y][ac[x][y]]<=y?0:sch(x,y+1,chg(st,ac[x][y],y)); 15 int ans=0,cnt15=0,tp=1; 16 for(int i=1;i<=k;++i)if(!ok[x][y][i])if(lp[x][y][i]>y&&lp[x][y][i]!=15)ans=mo(ans+sch(x,y+1,chg(st,i,y)));else if(lp[x][y][i]==15)cnt15++,tp=i; 17 if(cnt15)ans=(ans+cnt15*1ll*sch(x,y+1,chg(st,tp,y)))%mod; 18 return M[x][y][st]=ans; 19 } 20 int main(){ 21 cin>>n>>m>>k; 22 if(n+m-1>k)return puts("0"),0; 23 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){ 24 cin>>ac[i][j]; 25 for(int x=1;x<=i;++x)for(int y=1;y<=j;++y)ok[x][y][ac[i][j]]=1; 26 ok[i][j][ac[i][j]]=0; 27 } 28 long long prest=(1ll<<(k<<2))-1; 29 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(ac[i][j])prest=chg(prest,ac[i][j],14); 30 cout<<sch(1,1,prest)<<endl; 31 }
T3:回忆树
大意:给定trie,每次询问一条路径上某个字符串出现多少次。$n,m \le 100000, \sum |S| \ le 300000$
很恶心的大神题。思路不是很难想但是真的不好码。
首先直接找出路径然后询问复杂度肯定升天了,但是不难发现其实带拐弯的询问能拆成比较好做的几部分
首先是一条上来的链,然后是以拐点为中心的两侧长各为$|S|-1$的链,然后又是一条下去的链。
跨过拐点的部分看似和原问题是一样的,但是因为总询问串长不大,所以做暴力的话复杂度也是对的(哈希或kmp)。
只要能在合法的复杂度之内找到两点到lca路径上的某一段就可以。倍增然后暴力跳父亲,复杂度肯定没问题。
现在就剩两条链的事了。因为询问是有方向的,所以上下两条链貌似不一样,但是其实只要在处理上来的那条链的时候把询问串翻转一下就和下去的那条链一样。现在问题就是考虑怎么弄一条链。
这样其实就是很经典的字符串匹配,但是是树上的多条链和多个模式串进行匹配,怎么做?
直观上有两个想法:
1)树上字符串,广义SAM直接来啊:
这也是我考场上想到的,但是直接放弃了。
利用endpos集合的含义,其实问题就转变成了求一个字符串在广义SAM上匹配之后,有多少个endpos在指定链上。
这个想法看起来简单粗暴直接好理解,但是细思极恐。
按照之前那些题的套路,树上维护endpos,那不就是线段树合并了吗。。?
然后这个东西问的还是链上的,所以要快速求和,再写个树链剖分呗。。。
这么多硬菜加在一起,轻轻松松4k起步,写出来也要调到暴毙。。。
然后我果断没有写这个因为估计考场上写不完。考后又在寻找更简单一点的方法。
2)没有花哨的字符串多串匹配当然首选AC自动机啊:
可能是太久没用到了所以有些生疏。还回头看了看板子(虽说自己默写的其实没错)
对于已经给定的那个trie建AC自动机并没有什么用。因为它又不是模式串。
模式串是询问里的那些啊。。。然而居然没有强制在线?那不好说?离线下来建个AC自动机呗。。。
(然而别忘了因为要处理向上的链,所以反串也要塞进AC自动机里。。)
树上匹配字符串在AC自动机上跑也没问题啊,直接来就行。
但是是链上,咋整?这个链已经被转化成了一个点到根的路径的一部分了。
到根上的好求啊,直接从根开始匹配然后dfs前缀和累加就好了啊。
那么到根的路径的一部分其实就是两个到根的路径做差呗。。。经典的差分思路。
然而看一下我们要询问的是啥?某个串出现次数。也就是在原trie祖先链上每个节点对应的AC自动机上节点在当前节点对应的AC自动机节点的子树内的出现次数和。
单点加。询问子树和。
第一想法是LCT。当场暴毙。
哪里有那么麻烦,直接拍在dfn序上,单点加就单点加,子树求和就是查区间。
树状数组维护。这有何难?
差分什么的拿vector存一下然后放在trie上dfs跑就好了。进这个点时加上AC自动机对应点的贡献,回溯时再删掉。
但是要注意这个差分,例如对于u-lca,并不能简单的在u加在lca减。因为在lca处及靠下一点的部分其实与更靠上的节点也产生了贡献。
例如:1a2a3a4a5b6a7a8a9b10a11a12a13a14a15。查询7-15上aa出现次数。
7和15的lca是7啊。然而如果你在7处减的话你会把6a7a8这个给算上。(这东西在8节点产生的贡献没有在7被扣掉)
这里写错直接爆零。。。手模了好多小样例都不错,被这玩意卡了一下调了好久。。。
(然而找样例用了一个小时改正也就十几分钟。。。当我发现这个能卡我的样例时高兴坏了)
所以应该是在lca向u走len-1步之后的那个节点减去贡献。
在写暴力的时候顺便把这个加进去就好了,因为经过lca的那段的暴力哈希当然也要用到这个节点。
然后想偷懒的我选择了这个方法,在仔细思考之后发现哈希+树状数组+倍增lca/暴跳+若干dfs貌似代码也不会好到哪里去。。。
于是暴写一下午+半晚上写出了这个难得一见的80+行的代码。
因为没写完被认为在颓废还被查了几次水表。。。自闭。。。
终于在大半夜AC了这道题。写个博客题解当解题报告。算是比较圆满的结束了这一天。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 666666 4 int n,m,c[26][S],f[S],fir[S],l[S],to[S],v[S],ec,ch[S],pc,rt; 5 int R[S],q[S],ans[S],F[20][S],dep[S],fv[S];char s[S]; 6 vector<int>ap[S],Rt[S]; 7 void link(int a,int b,int va){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=va;} 8 void DFS(int p){ 9 dep[p]=dep[F[0][p]]+1;for(int i=1;i<19;++i)F[i][p]=F[i-1][F[i-1][p]]; 10 for(int i=fir[p];i;i=l[i])if(to[i]!=F[0][p])F[0][to[i]]=p,DFS(to[i]),fv[to[i]]=v[i]; 11 } 12 int lca(int a,int b){ 13 if(dep[a]<dep[b])swap(a,b); 14 int sub=dep[a]-dep[b]; 15 for(int i=18;~i;--i)if(sub&1<<i)a=F[i][a]; 16 if(a==b)return a; 17 for(int i=18;~i;--i)if(F[i][a]!=F[i][b])a=F[i][a],b=F[i][b]; 18 return F[0][a]; 19 } 20 void insert(int al,int&p,int o){ 21 if(!p)p=++pc; 22 if(!s[al]){R[o]=p;return;} 23 insert(al+1,c[s[al]-'a'][p],o); 24 } 25 void buildAC(){ 26 q[1]=1;for(int i=0;i<26;++i)c[i][0]=1;f[0]=1; 27 for(int h=1,t=1;h<=t;++h)for(int i=0;i<26;++i) 28 if(c[i][q[h]])f[q[++t]=c[i][q[h]]]=c[i][f[q[h]]]; 29 else c[i][q[h]]=c[i][f[q[h]]]; 30 } 31 int Fir[S],Ec,L[S],To[S],dfn[S],dfr[S],tim; 32 void Link(int a,int b){L[++Ec]=Fir[a];Fir[a]=Ec;To[Ec]=b;} 33 void dfs(int p){ 34 dfn[p]=++tim; 35 for(int i=Fir[p];i;i=L[i])dfs(To[i]); 36 dfr[p]=tim; 37 } 38 int t[S]; 39 void add(int p,int w){for(;p<S;p+=p&-p)t[p]+=w;} 40 int Ask(int p,int a=0){for(;p;p^=p&-p)a+=t[p];return a;} 41 int ask(int p){return Ask(dfr[p])-Ask(dfn[p]-1);} 42 void dfsans(int p,int acp){ 43 add(dfn[acp],1); 44 //for(int i=1;i<=tim;++i)cout<<Ask(i)-Ask(i-1)<<' ';cout<<endl; 45 for(int i=0;i<ap[p].size();++i)ans[ap[p][i]>>1]+=Rt[p][i]*ask(R[ap[p][i]]); 46 for(int i=fir[p];i;i=l[i])if(to[i]!=F[0][p])dfsans(to[i],c[v[i]][acp]); 47 add(dfn[acp],-1); 48 } 49 void ins(int p,int o,int r){ap[p].push_back(o);Rt[p].push_back(r);} 50 unsigned long long hsh[S],pw[S],rh; 51 int spj(int a,int b,int c,int o,int x=0){ 52 int len=strlen(s),tp=rh=0,sub=max(dep[a]-dep[c]-len+1,0); 53 for(int i=0;i<len;++i)rh=rh*31+s[i]-'a'; 54 for(int i=18;~i;--i)if(sub&1<<i)a=F[i][a]; 55 ins(a,o<<1,-1); 56 while(a!=c)++tp,hsh[tp]=fv[a],a=F[0][a]; 57 sub=max(dep[b]-dep[c]-len+1,0); 58 for(int i=18;~i;--i)if(sub&1<<i)b=F[i][b]; 59 ins(b,o<<1|1,-1); 60 a=b;tp+=dep[b]-dep[c]; 61 while(b!=c)hsh[tp]=fv[b],tp--,b=F[0][b]; 62 tp+=dep[a]-dep[c]; 63 for(int i=1;i<=tp;++i)hsh[i]+=hsh[i-1]*31,pw[i]=pw[i-1]*31; 64 for(int i=len;i<=tp;++i)x+=rh==hsh[i]-hsh[i-len]*pw[len]; 65 return x; 66 } 67 int main(){//freopen("1.in","r",stdin); 68 cin>>n>>m;pw[0]=1; 69 for(int i=1,a,b;i<n;++i)scanf("%d%d%s",&a,&b,s),link(a,b,s[0]-'a'),link(b,a,s[0]-'a'); 70 DFS(1); 71 for(int i=1,x,y,c;i<=m;++i){ 72 scanf("%d%d%s",&x,&y,s);c=lca(x,y); 73 ins(x,i<<1,1);ins(y,i<<1|1,1);ans[i]=spj(x,y,c,i); 74 insert(0,rt,i<<1|1); 75 reverse(s,s+strlen(s)); 76 insert(0,rt,i<<1); 77 }buildAC(); 78 for(int i=1;i<=pc;++i)Link(f[i],i); 79 dfs(0); dfsans(1,rt); 80 for(int i=1;i<=m;++i)printf("%d\n",ans[i]); 81 }
自勉:在家集训10天别再颓了。
2020/01/29 23:50