Loading

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();}
View Code

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();}
View Code

至于查找前驱后继之类的操作可以用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();}
View Code

 

posted @ 2021-07-16 15:34  雪域亡魂  阅读(73)  评论(0编辑  收藏  举报