Noip模拟14 2021.7.13
T1 队长快跑
本身dp就不强的小马看到这题并未反映过来是个dp(可能是跟题面太过于像那个黑题的队长快跑相似)
总之,基础dp也没搞出来,不过这题倒是启发了小马以后考试要往dp哪里想想
$dp_{i,S}$表示处理到i号水晶,其中选择的要摧毁的水晶A最小为S
正解思路应先考虑出$O(n^3)$的$dp$方程:
$(A_i\leq B_i)dp_{i,A_i}=max(dp_{i-1,B_i+1},dp_{i-1,B_i+2}...dp_{i-1,MAX})+1$
$(A_i>B_i)dp_{i,A_i}=max(dp_{i-1,A_i+1},dp_{i-1,A_i+2}...dp_{i-1,MAX})+1$
$dp_{i,j}=dp_{i-1,j}+1$其中$j\epsilon (B_i,A_i]$
这样的话转移需要$O(n^3)$复杂度
考虑优化
发现$dp$方程可以对应线段树操作,即
单点修改,区间查询等。
1 #include<bits/stdc++.h> 2 #define int long long 3 #define lid (id<<1) 4 #define rid (id<<1|1) 5 using namespace std; 6 inline int read(){ 7 int x=0,f=1; char ch=getchar(); 8 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 9 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 10 return x*f; 11 } 12 int n,a[8000000],b[8000000],dis[8000000],cnt,neww; 13 struct SNOWtree{ 14 int ll[16000000],rr[16000000]; 15 int maxn[16000000],lzy[16000000]; 16 inline void pushdown(int id){ 17 if(!lzy[id]||ll[id]==rr[id]) return; 18 maxn[lid]+=lzy[id]; maxn[rid]+=lzy[id]; 19 lzy[lid]+=lzy[id]; lzy[rid]+=lzy[id]; 20 lzy[id]=0; 21 } 22 void build(int id,int l,int r){ 23 ll[id]=l; rr[id]=r; 24 if(l==r) return; 25 int mid=(l+r)>>1; 26 build(lid,l,mid); 27 build(rid,mid+1,r); 28 } 29 void update(int id,int l,int r,int v){ 30 if(l<=ll[id]&&rr[id]<=r){ 31 maxn[id]+=v; lzy[id]+=v; 32 return; 33 } 34 pushdown(id); 35 int mid=(ll[id]+rr[id])>>1; 36 if(l<=mid) update(lid,l,r,v); 37 if(r>mid) update(rid,l,r,v); 38 maxn[id]=max(maxn[lid],maxn[rid]); 39 } 40 void update_sg(int id,int x,int v){ 41 if(ll[id]==rr[id]){ 42 maxn[id]=max(maxn[id],v); 43 return; 44 } 45 pushdown(id); 46 int mid=(ll[id]+rr[id])>>1; 47 if(x<=mid) update_sg(lid,x,v); 48 else update_sg(rid,x,v); 49 maxn[id]=max(maxn[lid],maxn[rid]); 50 } 51 int query(int id,int l,int r){ 52 if(l<=ll[id]&&rr[id]<=r) return maxn[id]; 53 pushdown(id); 54 int mid=(ll[id]+rr[id])>>1,ans=0; 55 if(l<=mid) ans=max(ans,query(lid,l,r)); 56 if(r>mid) ans=max(ans,query(rid,l,r)); 57 return ans; 58 } 59 }tr; 60 namespace WSN{ 61 inline int main(){ 62 n=read(); 63 for(int i=1;i<=n;i++){ 64 dis[++cnt]=a[i]=read(); 65 dis[++cnt]=b[i]=read(); 66 } 67 sort(dis+1,dis+cnt+1); 68 neww=unique(dis+1,dis+cnt+1)-dis-1; 69 for(int i=1;i<=n;i++){ 70 a[i]=lower_bound(dis+1,dis+neww+1,a[i])-dis; 71 b[i]=lower_bound(dis+1,dis+neww+1,b[i])-dis; 72 } 73 tr.build(1,1,neww); 74 for(int i=1;i<=n;i++) 75 if(a[i]<=b[i]) 76 tr.update_sg(1,a[i],(tr.query(1,b[i]+1,neww)+1)); 77 else{ 78 tr.update(1,(b[i]+1),a[i],1); 79 tr.update_sg(1,a[i],(tr.query(1,a[i]+1,neww)+1)); 80 } 81 printf("%lld\n",tr.maxn[1]); 82 return 0; 83 } 84 } 85 signed main(){return WSN::main();}
T2 影魔
一看这题目,直接傻掉,当时做线段树合并的时候就看到过一道影魔
博主说线段树合并里较难的一个叫队长快跑,一个叫影魔。。。。
然而,题目却并不一样
现在想想,这提应该没有那个难
首先要理解清楚题目让你球的是什么东西(不过估计也只有我把题意理解成加法计算。)
然而他给的灵魂种类都只是一个种类,代表一种人,
最后让统计的是不同的数字有几个,并非加法,而是个数。
那么我们先考虑树链剖分求出距离和LCA,这个比较好想
然后就是超纲知识——主席树(可持久化线段树)。
我们用点的深度为版本开主席树。
先把每个点的深度按从小到大的顺序排序,深度一样的点放进一颗主席树中,具体操作用vector存一下循环便利即可
note:一定注意对应关系!!即vector中插的是dfn序
然后依次查找插入节点的前驱,后继
并分别两两找到其对应的LCA
1.前驱与插入点的LCA-1
2.后继与插入点的LCA-1
3.前驱与后继的LCA+1
注意判断如果前驱或后继是哨兵(inf)那就是没有前/后点,跳过本此操作即可
然后对于每次询问先框定其询问的深度范围,超过最深点的版本默认最深,查找就行。
1 #include<bits/stdc++.h> 2 #define lid (id<<1) 3 #define rid (id<<1|1) 4 #define lc t[i].ch[0] 5 #define rc t[i].ch[1] 6 #define LC t[j].ch[0] 7 #define RC t[j].ch[1] 8 using namespace std; 9 inline int min(int a,int b){return a<b?a:b;} 10 inline int max(int a,int b){return a>b?a:b;} 11 inline bool cmp(int a,int b){return a<b;} 12 inline void swap(int &a,int &b){a^=b^=a^=b;} 13 inline int read(){ 14 int x=0,f=1; char ch=getchar(); 15 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 16 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 17 return x*f; 18 } 19 const int NN=8000000,inf=0x7fffffff; 20 vector<int> deep[NN]; 21 struct SNOW{int from,to,next;}; SNOW e[NN]; int head[NN],rp; 22 struct hjt{int v,ch[2];}t[NN]; 23 struct Splay{int fa,son[2],siz,val;}s[NN]; 24 int n,m,c[NN],dep_max,tot; 25 int dfn[NN],rk[NN],son[NN],fa[NN],dep[NN],siz[NN],top[NN],tmp; 26 int cnt,num,rt[NN]; 27 inline void add(int x,int y){ 28 e[++rp]=(SNOW){x,y,head[x]};head[x]=rp; 29 e[++rp]=(SNOW){y,x,head[y]};head[y]=rp; 30 } 31 inline void dfs1(int f,int x){ 32 fa[x]=f; dep[x]=dep[f]+1; siz[x]=1; 33 for(int i=head[x];i;i=e[i].next){ 34 int y=e[i].to; 35 if(y==f) continue; 36 dfs1(x,y); 37 siz[x]+=siz[y]; 38 if(siz[son[x]]<siz[y]) son[x]=y; 39 } 40 } 41 inline void dfs2(int x,int t){ 42 top[x]=t; dfn[x]=++tmp; rk[tmp]=x; 43 if(!son[x]) return; 44 dfs2(son[x],t); 45 for(int i=head[x];i;i=e[i].next){ 46 int y=e[i].to; 47 if(y!=fa[x]&&y!=son[x]) dfs2(y,y); 48 } 49 } 50 inline int LCA(int x,int y){ 51 while(top[x]!=top[y]){ 52 if(dep[top[x]]<dep[top[y]]) swap(x,y); 53 x=fa[top[x]]; 54 } 55 if(dfn[x]>dfn[y]) swap(x,y); 56 return x; 57 } 58 void update(int &i,int j,int l,int r,int pos,int val){ 59 i=++num; lc=LC; rc=RC; t[i].v=t[j].v; 60 if(l==r){t[i].v+=val;return;} 61 int mid=l+r>>1; 62 if(pos<=mid) update(lc,LC,l,mid,pos,val); 63 else update(rc,RC,mid+1,r,pos,val); 64 t[i].v=t[lc].v+t[rc].v; 65 } 66 int query(int i,int l,int r,int L,int R){ 67 if(!i||L>R) return 0; 68 if(l==L&&R==r) return t[i].v; 69 int mid=l+r>>1; 70 if(R<=mid) return query(lc,l,mid,L,R); 71 if(L>mid) return query(rc,mid+1,r,L,R); 72 return query(lc,l,mid,L,mid)+query(rc,mid+1,r,mid+1,R); 73 } 74 struct SPLAY{ 75 int root; 76 void pushup(int x){ 77 s[x].siz=s[s[x].son[0]].siz+s[s[x].son[1]].siz+1; 78 } 79 int get(int x){ 80 return x==s[s[x].fa].son[1]; 81 } 82 void rotate(int x){ 83 int y=s[x].fa,z=s[y].fa,xpos=get(x),ypos=get(y); 84 s[z].son[ypos]=x; s[x].fa=z; 85 s[y].son[xpos]=s[x].son[xpos^1]; s[s[x].son[xpos^1]].fa=y; 86 s[x].son[xpos^1]=y; s[y].fa=x; 87 pushup(y); pushup(x); 88 } 89 void splay(int x,int goal){ 90 while(s[x].fa!=goal){ 91 int y=s[x].fa,z=s[y].fa,xpos=get(x),ypos=get(y); 92 if(z!=goal){ 93 if(xpos==ypos) rotate(y); 94 else rotate(x); 95 } 96 rotate(x); 97 } 98 if(!goal) root=x; 99 } 100 void insert(int val){ 101 int u=root,ff=0; 102 while(u&&s[u].val!=val) 103 ff=u,u=s[u].son[val>s[u].val]; 104 u=++tot; 105 if(ff) s[ff].son[val>s[ff].val]=u; 106 s[u].son[0]=s[u].son[1]=0; 107 s[u].fa=ff;s[u].val=val; 108 s[u].siz=1; 109 splay(u,0); 110 } 111 void find_rank(int val){ 112 int u=root; 113 if(!u) return; 114 while(s[u].son[val>s[u].val] && val!=s[u].val) u=s[u].son[val>s[u].val]; 115 splay(u,0); 116 } 117 int prenxt(int val,int op){ 118 find_rank(val); 119 int u=root; 120 if(s[u].val>val && op) return s[u].val; 121 if(s[u].val<val &&!op) return s[u].val; 122 u=s[u].son[op]; 123 while(s[u].son[op^1]) u=s[u].son[op^1]; 124 return s[u].val; 125 } 126 };SPLAY que[NN]; 127 namespace WSN{ 128 inline int main(){ 129 // FILE *A=freopen("1.in","r",stdin); 130 // FILE *B=freopen("1.out","w",stdout); 131 n=read();m=read(); 132 for(int i=1;i<=n;i++){ 133 c[i]=read(); 134 que[c[i]].insert(-inf); 135 que[c[i]].insert(inf); 136 } 137 for(int i=1;i<n;i++){int fa=read(); add(fa,i+1);} 138 dfs1(0,1); dfs2(1,1); 139 for(int i=1;i<=n;i++){ 140 deep[dep[i]].push_back(dfn[i]); 141 dep_max=max(dep_max,dep[i]); 142 } 143 for(int i=1;i<=dep_max;i++){ 144 for(int j=0;j<deep[i].size();j++){ 145 int p=!j?i-1:i; 146 int node=deep[i][j],pre,nxt,color=c[rk[node]]; 147 que[color].insert(node); 148 pre=que[color].prenxt(node,0); 149 nxt=que[color].prenxt(node,1); 150 update(rt[i],rt[p],1,n,node,1); 151 if(pre!=inf&&pre!=-inf){ 152 int lca=LCA(rk[pre],rk[node]); 153 update(rt[i],rt[i],1,n,dfn[lca],-1); 154 } 155 if(nxt!=inf&&nxt!=-inf){ 156 int lca=LCA(rk[nxt],rk[node]); 157 update(rt[i],rt[i],1,n,dfn[lca],-1); 158 } 159 if(pre!=inf&&pre!=-inf&&nxt!=inf&&nxt!=-inf){ 160 int lca=LCA(rk[pre],rk[nxt]); 161 update(rt[i],rt[i],1,n,dfn[lca],1); 162 } 163 } 164 } 165 while(m--){ 166 int U=read(),D=read(); 167 int range=min(dep[U]+D,dep_max); 168 int ans=query(rt[range],1,n,dfn[U],dfn[U]+siz[U]-1); 169 printf("%d\n",ans); 170 } 171 return 0; 172 } 173 } 174 signed main(){return WSN::main();}
至于查找前驱后继之类的操作可以用set比较方便,但是看一旁的zxs和JYFHYX同学磕指针很费劲的样子,于是便悄咪咪的打了splay,4.4K代码超爽
T3 抛硬币
又一个dp题,然而这次集训一次没接触导致啥也想不出来好吧。。
艾,小马的dp还是太弱了
看题发现dp可以$O(S^2)$跑,非常舒服
设$f_{i,j}$表示处理完S的前i个位置,长度为j的本质不同子序列个数
如果尾部添加一个字符,最先想到$f_{i,j}=f_{i-1,j}+f_{i-1,j-1}$
然后减去这样会算重的个数
发现算重的部分一定是结尾为$S_i$的串
则假设$S_i$上一次出现的位置为p,那么算重的串个数即为$f_{p-1,j-1}$
那么直接$n^2$转移即可。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 inline int read(){ 5 int x=0,f=1; char ch=getchar(); 6 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 7 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 8 return x*f; 9 } 10 const int NN=5005,p=998244353; 11 int l,pre[NN],vis[30],f[NN][NN]; 12 char s[NN]; 13 namespace WSN{ 14 inline int main(){ 15 scanf("%s",s+1); l=read(); 16 int n=strlen(s+1); 17 for(int i=1;i<=n;i++){ 18 int ch=s[i]-'a'; 19 if(!vis[ch]){ 20 vis[ch]=i; 21 } 22 else{ 23 pre[i]=vis[ch]; 24 vis[ch]=i; 25 } 26 } 27 for(int i=0;i<=n;i++) f[i][0]=1; 28 for(int i=1;i<=n;i++) 29 for(int j=1;j<=l;j++) 30 if(!pre[i]) f[i][j]=(f[i-1][j]+f[i-1][j-1])%p; 31 else f[i][j]=(f[i-1][j]+f[i-1][j-1]-f[pre[i]-1][j-1]+p)%p; 32 printf("%lld\n",f[n][l]); 33 return 0; 34 } 35 } 36 signed main(){return WSN::main();}