[考试反思]0118省选模拟11:迟缓
Flag:下次AKt再半夜3点打呼噜给我弄醒我就下床拿枕头捶醒他
困死了啊啊啊啊啊啊啊
状态极差,写了三个暴力,根本没动脑子,也不想动,也不能动
所以就在考场上把昨天T2给水过了
T1:组合数问题
题意:给定$n$个数从中任意选子集乘积不超过$k$的方案数。
正解还不会,但是记录一些搜索剪枝的技巧。
预先排序肯定是要有的。相同元素压在i一起,特殊考虑特殊元素(如1以及大于根号的)
预处理$lower \ bound$值,及时返回跳出,以及搜索顺序的问题。
然而我的搜索还是不很优。。。虽说我感觉和正解有那么一点点相像
但是完全看不懂正解,鸽了
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 998244353 4 map<int,int>r;unordered_map<int,int>dp[222222]; 5 int n,k,s[222222],t[222222],lwb[22222222],fac[222222],inv[2222222],cnt1; 6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 7 int C(int b,int t){return 1ll*fac[b]*inv[t]%mod*inv[b-t]%mod;} 8 int sch(int n,int k){ 9 if(n==0||k<s[1])return 1; 10 if(k<=20000000&&n>lwb[k])n=lwb[k]; 11 if(dp[n].find(k)!=dp[n].end())return dp[n][k]; 12 int rk=k,ans=0; 13 for(int c=0;c<=t[n]&&rk;++c,rk/=s[n])ans=(ans+1ll*sch(n-1,rk)*C(t[n],c))%mod; 14 return dp[n][k]=ans; 15 } 16 int main(){ 17 cin>>n>>k; 18 fac[0]=1; for(int i=1;i<=200000;++i)fac[i]=1ll*fac[i-1]*i%mod; 19 inv[200000]=qp(fac[200000],mod-2); 20 for(int i=199999;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod; 21 for(int i=1,x;i<=n;++i){scanf("%d",&x);if(x==1)cnt1++;else r[x]++;} 22 n=0;for(auto it:r)s[++n]=it.first,t[n]=it.second; 23 for(int i=1;i<=n;++i){ 24 long long x=1; 25 for(int j=1;j<=t[i];++j){x*=s[i];if(x>k)t[i]=j-1;break;} 26 } 27 for(int i=1;i<=n&&s[i]<=20000000;++i)lwb[s[i]]=i; 28 for(int i=1;i<=20000000;++i)if(!lwb[i])lwb[i]=lwb[i-1]; 29 cout<<1ll*sch(n,k)*qp(2,cnt1)%mod<<endl; 30 }
T2:recollection
大意:给定前缀trie,求$max(lcs+lcp)$。$m \le 200000$
既然已经是前缀$trie$了那么$lcp$就已经是$dep_{lca}$了。
字符串题,要求$lcs$,既然是后缀那就上$SAM$呗。
勉强算一个广义,这道题可以按照$dfs$建树然而复杂度不对。。。
现在问题就是给你两棵树求最大的$lca$深度和的点对。
对于其中一棵树按照$dfn$排个序的话所有的$lca$都是相邻两点的$lca$。距离越近$lca$越深。
这时候我们在另一棵树上$dfs$,每个点最初只有自己这个节点,线段树合并维护子树和,维护信息时顺便$ST$表查询最优的$lca$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 400004 4 unordered_map<int,int>c[S]; 5 int pc=1,f[S],len[S],lst=1,ans,n; 6 int extend(int C){ 7 int p=lst,np=lst=++pc,q,nq; len[np]=len[p]+1; 8 for(;p&&!c[p][C];p=f[p])c[p][C]=np; 9 if(!p)return f[np]=1,lst; 10 if(len[q=c[p][C]]==len[p]+1)return f[np]=q,lst; 11 nq=++pc; len[nq]=len[p]+1; c[nq]=c[q]; 12 f[nq]=f[q]; f[q]=f[np]=nq; 13 for(;c[p][C]==q;p=f[p])c[p][C]=nq; 14 return lst; 15 } 16 int l[S],to[S],v[S],ec,fir[S],dfn[S],t,eu[S],dep[S],rp[S],Rp[S]; 17 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;} 18 void dfs(int p,int nc){ 19 int tp=rp[p]=p==1?1:extend(nc); eu[++t]=p; dfn[p]=t; 20 for(int i=fir[p];i;i=l[i])lst=tp,dep[to[i]]=dep[p]+1,dfs(to[i],v[i]),eu[++t]=p; 21 } 22 int ST[20][S],lb[S],ref[S]; 23 void ST_init(){ 24 for(int i=1;i<=t;++i)ST[0][i]=dep[eu[i]]; 25 for(int i=1;i<20;++i)for(int j=1<<i;j<1<<i+1&&j<S;++j)lb[j]=i; 26 for(int i=1;i<20;++i)for(int j=1;j+(1<<i)-1<=t;++j) 27 ST[i][j]=min(ST[i-1][j],ST[i-1][j+(1<<i-1)]); 28 } 29 int deplca(int x,int y){ 30 if(y<x)swap(x,y); 31 int B=lb[y-x+1]; 32 return min(ST[B][x],ST[B][y-(1<<B)+1]); 33 } 34 int Fir[S],La[S],To[S],Ec,rt[S],lc[S<<5],rc[S<<5],L[S<<5],R[S<<5],w[S<<5],spc; 35 void Link(int a,int b){La[++Ec]=Fir[a];Fir[a]=Ec;To[Ec]=b;} 36 #define md (cl+cr>>1) 37 void insert(int&p,int v,int cl=1,int cr=t){ 38 p=++spc;L[p]=R[p]=v; 39 if(cl==cr)return; 40 if(v<=md)insert(lc[p],v,cl,md);else insert(rc[p],v,md+1,cr); 41 } 42 void merge(int&p,int cp,int cl=1,int cr=t){ 43 if(!cp)return; 44 if(!p){p=cp;return;} 45 merge(lc[p],lc[cp],cl,md);merge(rc[p],rc[cp],md+1,cr); 46 if(!lc[p])L[p]=L[rc[p]],R[p]=R[rc[p]],w[p]=w[rc[p]]; 47 else if(!rc[p])L[p]=L[lc[p]],R[p]=R[lc[p]],w[p]=w[lc[p]]; 48 else L[p]=L[lc[p]],R[p]=R[rc[p]],w[p]=max(w[lc[p]],max(w[rc[p]],deplca(R[lc[p]],L[rc[p]]))); 49 } 50 void Dfs(int p){ 51 if(Rp[p])insert(rt[p],dfn[Rp[p]]); 52 for(int i=Fir[p];i;i=La[i])Dfs(To[i]),merge(rt[p],rt[To[i]]); 53 ans=max(ans,len[p]+w[rt[p]]); 54 } 55 int main(){ 56 cin>>n; 57 for(int i=2,a,x;i<=n;++i)scanf("%d%d",&a,&x),link(a,i,x); 58 dfs(1,0);ST_init(); 59 for(int i=1;i<=t;++i)Rp[rp[i]]=i; 60 for(int i=2;i<=pc;++i)Link(f[i],i); 61 Dfs(1);cout<<ans<<endl; 62 }
T3:comparision
大意:给定两个二元组(0,0),(n+1,n+1)。0可以分裂为(0,0),n+1同理。每次操作将两个已有集分别作为两关键字加入,求新集合在所有已有集中的排名。
$n \le 50000$
直觉:插入,查排名。像是平衡树,但是不敢写,然而其实也没有那么难写。
每次插入前二分新集合的排名,找到排名为$mid$的元素把它$splay$上来,分别比较它的两个元素和新集合的两个元素。
比较的方法也很草率。只要分别找到两个集合的排名就好。这个排名是在$splay$上动态维护的。
如果有多个完全一样的集合,我把它们都合并了。
记录一下每个集合对应$splay$上的哪一个节点(方便把它直接$splay$到根)
也记录一下$splay$上每个节点的代表元素,如果有多个随便记一个(方便获知它的两个元素进行比较)
然后就剩个$splay$上查排名对应的数了。写单旋没被卡。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 55555 4 int tp[S],a[S],b[S],n,sz[S],pc,val[S],c[2][S],f[S],rt,A[S],B[S]; 5 void up(int p){sz[p]=sz[c[0][p]]+sz[c[1][p]]+val[p];} 6 void spin(int p){ 7 int F=f[p],G=f[F],D=c[1][F]==p,B=c[!D][p]; 8 if(G)c[c[1][G]==F][G]=p; c[!D][p]=F; c[D][F]=B; 9 if(B)f[B]=F; f[f[F]=p]=G; up(F); 10 } 11 void splay(int p){for(int F;F=f[p];spin(p));up(rt=p);} 12 #define lc c[0][p] 13 void find(int p,int rk){ 14 if(rk<=sz[lc])find(lc,rk); 15 else if(rk<=sz[lc]+val[p])splay(p); 16 else find(c[1][p],rk-sz[lc]-val[p]); 17 } 18 int Rank(int p){splay(tp[p]);return val[rt]+sz[c[0][rt]];} 19 bool cmp(int rk,int Ra,int Rb){ 20 find(rt,rk);int ra=A[rt],rb=B[rt];ra=Rank(ra); 21 return ra!=Ra?ra<Ra:Rank(rb)<=Rb; 22 } 23 int main(){//freopen("1.in","r",stdin); 24 cin>>n; rt=2; pc=2; 25 tp[a[n+2]=b[n+2]=A[2]=B[2]=n+2]=2; tp[a[n+1]=b[n+1]=A[1]=B[1]=n+1]=1; 26 val[1]=val[2]=1; c[1][2]=1; f[1]=2; up(1); up(2); 27 for(int i=1;i<=n;++i){ 28 scanf("%d%d",&a[i],&b[i]); 29 if(!a[i])a[i]=n+2;if(!b[i])b[i]=n+2; 30 int l=1,r=i+1,ans,ra=Rank(a[i]),rb=Rank(b[i]),rp; 31 while(l<=r)if(cmp(l+r>>1,ra,rb))l=ans=l+r>>1,l++; 32 else r=(l+r>>1)-1; 33 find(rt,ans); 34 int Ra=A[rt],Rb=B[rt];Ra=Rank(Ra);Rb=Rank(Rb); 35 find(rt,ans); 36 if(Ra==ra&&Rb==rb)val[rt]++,up(rt),tp[i]=rt; 37 else rp=c[1][rt],c[1][rt]=++pc,f[pc]=rt,c[1][pc]=rp,f[rp]=pc,val[pc]=1,A[pc]=a[i],B[pc]=b[i],tp[i]=pc,up(pc),up(rt); 38 printf("%d\n",Rank(i)); 39 } 40 }
.