省选模拟七 题解

写在前面:

这次考试的策略还是蛮正确的

发现了$T2$是水题后先开了$T3$的$60pts$暴力

剩下时间连打带调外加考场刚好用完时间

但可惜的是$T1dp$求两点之间最小代价由于转移出环被弃掉了

其实用$bfs$求最小代价就可以$AC$了

实力不济

就没什么好说的

A. 翻转硬币

标签:

$bfs+$状压$dp$

题解:

先对序列差分

问题转化为每次可以同时异或两个点,求最小代价

同时消去两个点的代价可以用$bfs$预处理出来

源点的个数就是差分序列的$1$的个数

有了这个,状态便有了明显的层次性了

$k<=10$,也就是说点数$<=20$

直接状压$dp$即可

复杂度$O(2*k*n+(2*k)^2*2^{2*k})$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e4+10,INF=1e9;
 4 int cnt,n,k,m,dp[1<<20],b[N],a[N],id[N],dis[21][N];
 5 void bfs(int S)
 6 {
 7         memset(dis[S],-1,sizeof(dis[S]));
 8         queue<int>q;
 9         q.push(id[S]);
10         dis[S][id[S]]=0;
11         while(q.size())
12         {
13                 int x=q.front();
14                 q.pop();
15                 for(int i=1;i<=m;i++)
16                 {
17                         int y=x+a[i];
18                         if(y<=n+1&&dis[S][y]==-1)
19                         {
20                                 dis[S][y]=dis[S][x]+1;
21                                 q.push(y);
22                         }
23                         y-=a[i]*2;
24                         if(y>0&&dis[S][y]==-1)
25                         {
26                                 dis[S][y]=dis[S][x]+1;
27                                 q.push(y);
28                         }
29                 }
30         }
31 }
32 int dfs(int x)
33 {
34         if(!x) return 0;
35         if(dp[x]!=INF) return dp[x];
36         for(int i=0;i<cnt;i++)
37         {
38                 if(!(x&(1<<i))) continue;
39                 for(int j=0;j<cnt;j++)
40                 {
41                         if(i==j) continue;
42                         if(!(x&(1<<j))) continue;
43                         if(dis[i+1][id[j+1]]==-1) continue;
44                         dp[x]=min(dp[x],dfs(x^(1<<i)^(1<<j))+dis[i+1][id[j+1]]);
45                 }
46         }
47         return dp[x];
48 }
49 int main()
50 {
51         //freopen("1.in","r",stdin);
52         //freopen("1.out","w",stdout);
53         scanf("%d%d%d",&n,&k,&m);
54         for(int i=1,x;i<=k;i++) scanf("%d",&x),b[x]^=1,b[x+1]^=1;
55         for(int i=1;i<=m;i++) scanf("%d",&a[i]);
56         for(int i=1;i<=n+1;i++)
57         {
58                 if(b[i])
59                 {
60                         id[++cnt]=i;
61                         bfs(cnt);
62                 }
63         }
64         for(int i=0;i<(1<<cnt);i++) dp[i]=INF;
65         dfs((1<<cnt)-1);
66         dp[(1<<cnt)-1]=dp[(1<<cnt)-1]>=INF?-1:dp[(1<<cnt)-1];
67         printf("%d\n",dp[(1<<cnt)-1]);
68         return 0;
69 }

T1

B. 回文子串

标签:

线段树

题解:

$k<=50$

边界暴力修改

中间的贡献都是一样的,可以用线段树区间修改

复杂度$O(m*k^2)$

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=5e4+10;
  4 int S,ans,n,k,m;
  5 char s[N],t[5];
  6 struct SGT
  7 {
  8         #define ls k<<1
  9         #define rs k<<1|1
 10         int L[N*4],R[N*4],w[N*4],f[N*4];
 11         char q[N*4];
 12         void down(int k)
 13         {
 14                 w[ls]=(R[ls]-L[ls]+1)*f[k];
 15                 q[ls]=q[k];
 16                 w[rs]=(R[rs]-L[rs]+1)*f[k];
 17                 q[rs]=q[k];
 18                 f[ls]=f[rs]=f[k];
 19                 f[k]=-1;
 20         }
 21         void build(int k,int l,int r)
 22         {
 23                 L[k]=l,R[k]=r;
 24                 f[k]=-1;
 25                 if(l==r)
 26                 {
 27                         q[k]=s[l];
 28                         return;
 29                 }
 30                 int mid=(l+r)>>1;
 31                 build(ls,l,mid);
 32                 build(rs,mid+1,r);
 33         }
 34         void change(int k,int l,int r,int x,char y)
 35         {
 36                 if(L[k]>=l&&R[k]<=r)
 37                 {
 38                         w[k]=x*(R[k]-L[k]+1);
 39                         f[k]=x;
 40                         q[k]=y;
 41                         return;
 42                 }
 43                 if(f[k]!=-1) down(k);
 44                 int mid=(L[k]+R[k])>>1;
 45                 if(l<=mid) change(ls,l,r,x,y);
 46                 if(r>mid) change(rs,l,r,x,y);
 47                 w[k]=w[ls]+w[rs];
 48         }
 49         int query(int k,int l,int r)
 50         {
 51                 if(L[k]>=l&&R[k]<=r) return w[k];
 52                 if(f[k]!=-1) down(k);
 53                 int mid=(L[k]+R[k])>>1,sum=0;
 54                 if(l<=mid) sum+=query(ls,l,r);
 55                 if(r>mid) sum+=query(rs,l,r);
 56                 return sum;
 57         }
 58         char get(int k,int x)
 59         {
 60                 if(L[k]==R[k]) return q[k];
 61                 if(f[k]!=-1) down(k);
 62                 int mid=(L[k]+R[k])>>1;
 63                 if(x<=mid) return get(ls,x);
 64                 else return get(rs,x);
 65         }
 66         void clear(int k,int x,char y)
 67         {
 68                 if(L[k]==R[k])
 69                 {
 70                         q[k]=y;
 71                         return;
 72                 }
 73                 if(f[k]!=-1) down(k);
 74                 int mid=(L[k]+R[k])>>1;
 75                 if(x<=mid) clear(ls,x,y);
 76                 else clear(rs,x,y);
 77         }
 78 }T;
 79 void update(int x)
 80 {
 81         int L,R,ans=0;
 82         L=R=x;
 83         while(L>=1&&R<=n&&s[L]==s[R]&&R-L+1<=k) L--,R++,ans++;
 84         L=x,R=x+1;
 85         while(L>=1&&R<=n&&s[L]==s[R]&&R-L+1<=k) L--,R++,ans++;
 86         T.change(1,x,x,ans,T.get(1,x));
 87 }
 88 int get(int l,int r,int x)
 89 {
 90         int L,R,ans=0;
 91         L=R=x;
 92         while(L>=l&&R<=r&&s[L]==s[R]&&R-L+1<=k) L--,R++,ans++;
 93         L=x,R=x+1;
 94         while(L>=l&&R<=r&&s[L]==s[R]&&R-L+1<=k) L--,R++,ans++;
 95         return ans;
 96 }
 97 int main()
 98 {
 99         //freopen("2.in","r",stdin);
100         //freopen("2.out","w",stdout);
101         scanf("%s%d%d",s+1,&k,&m);
102         n=strlen(s+1);
103         T.build(1,1,n);
104         for(int i=1;i<=n;i++) update(i);
105         S=(k+1)/2;
106         for(int i=1,opt,l,r;i<=m;i++)
107         {
108                 scanf("%d%d%d",&opt,&l,&r);
109                 if(opt==1)
110                 {
111                         scanf("%s",t);
112                         if(l+S+1>r-S-1)
113                         {
114                                 for(int j=l;j<=r;j++) T.clear(1,j,t[0]);
115                                 for(int j=max(1,l-S-k-1);j<=min(n,r+k+S+1);j++) s[j]=T.get(1,j);
116                                 for(int j=max(1,l-S);j<=min(n,r+S);j++) update(j);
117                         }
118                         else
119                         {
120                                 T.change(1,l+S+1,r-S-1,(k+1)/2+k/2,t[0]);
121                                 for(int j=l;j<=min(r-S-1,l+S);j++) T.clear(1,j,t[0]);
122                                 for(int j=max(l,r-S);j<=r;j++) T.clear(1,j,t[0]);
123                                 for(int j=max(1,l-S-k-1);j<=min(n,min(r-1,l+k+1+S));j++) s[j]=T.get(1,j);
124                                 for(int j=max(1,r-S-k);j<=min(n,r+S+1+k);j++) s[j]=T.get(1,j);
125                                 for(int j=max(1,l-S);j<=min(r-S-1,l+S);j++) update(j);
126                                 for(int j=max(1,r-S);j<=min(n,r+S);j++) update(j);
127                         }
128                 }
129                 else
130                 {
131                         ans=0;
132                         if(l+S>r)
133                         {
134                                 for(int j=max(1,l-k);j<=min(n,r+k);j++) s[j]=T.get(1,j);
135                                 for(int j=l;j<=r;j++) ans+=get(l,r,j);
136                         }
137                         else
138                         {
139                                 ans=l+S+1<=r-S-1?T.query(1,l+S+1,r-S-1):0;
140                                 for(int j=max(1,l-S);j<=min(r-1,l+S+S);j++) s[j]=T.get(1,j);
141                                 for(int j=max(1,max(l-k,r-S-k));j<=min(n,r+k);j++) s[j]=T.get(1,j);
142                                 for(int j=l;j<=min(r-S-1,l+S);j++) ans+=get(l,r,j);
143                                 for(int j=max(l,r-S);j<=r;j++) ans+=get(l,r,j);
144                         }
145                         printf("%d\n",ans);
146                 }
147         }
148         return 0;
149 }

T2

我的暴力循环边界其实是瞎写的,反正$4s$能跑过

C. 最大价值

标签:

平衡树优化$dp$

题解:

设$f[i][j]$代表考虑了$i$个点已经取了$j$个点的最大收益

有:

$$f[i][j]=max(f[i-1][j],f[i-1][j-1]+(j-1)*a[i]+b[i])$$

可以归纳打表证明出$f$的转移点是单调的

$$g[i][j]=f[i][j]-f[i][j-1]$$

x为第一个从后面转移的点

那么

$$j\in[1,x-1]\ g[i][j]=g[i-1][j]$$

$$j=x\ g[i][j]=(j-1)*a_i+b_i$$

$$j\in[x,i]\ g[i][j]=g[i-1][j-1]+a[i]$$

平衡树维护即可

  1 #include<bits/stdc++.h>
  2 #define int long long
  3 using namespace std;
  4 const int N=3e5+10;
  5 int n,g[N];
  6 struct node
  7 {
  8         int a,b;
  9         friend bool operator <(node l,node r)
 10         {
 11                 return l.a<r.a;
 12         }
 13 }f[N];
 14 int read()
 15 {
 16         int sum,k=1;char s;
 17         while(s=getchar(),s<‘0‘||s>‘9‘) if(s==‘-‘) k=-1;sum=s-‘0‘;
 18         while(s=getchar(),s>=‘0‘&&s<=‘9‘) sum=sum*10+s-‘0‘;
 19         return k*sum;
 20 }
 21 struct Splay
 22 {
 23         int cnt,root,st[N],ch[N][2],f[N],w[N],size[N],lazy[N];
 24         void pushup(int x)
 25         {
 26                 size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
 27         }
 28         void pushdown(int x)
 29         {
 30                 if(ch[x][0]) w[ch[x][0]]+=lazy[x],lazy[ch[x][0]]+=lazy[x];
 31                 if(ch[x][1]) w[ch[x][1]]+=lazy[x],lazy[ch[x][1]]+=lazy[x];
 32                 lazy[x]=0;
 33         }
 34         int get(int x)
 35         {
 36                 return ch[f[x]][1]==x;
 37         }
 38         void rotate(int x)
 39         {
 40                 int y=f[x],z=f[y],k=get(x),w=ch[x][k^1];
 41                 ch[y][k]=w;if(w) f[w]=y;
 42                 if(z) ch[z][ch[z][1]==y]=x;f[x]=z;
 43                 ch[x][k^1]=y;f[y]=x;
 44                 pushup(y),pushup(x);
 45         }
 46         void splay(int x,int goal)
 47         {
 48                 int top=0,y=x;
 49                 while(y^goal) st[++top]=y,y=f[y];
 50                 while(top) pushdown(st[top--]);
 51                 while(f[x]^goal)
 52                 {
 53                         int y=f[x],z=f[y];
 54                         if(z^goal) rotate(get(x)^get(y)?x:y);
 55                         rotate(x);
 56                 }
 57                 if(!goal) root=x;
 58         }
 59         void insert(int x,int y,int z,int typ)
 60         {
 61                 w[++cnt]=z;
 62                 size[cnt]=1;
 63                 if(x) size[x]++;
 64                 size[y]++;
 65                 ch[y][typ]=cnt;
 66                 f[cnt]=y;
 67                 splay(cnt,0);
 68         }
 69         int kth(int x)
 70         {
 71                 int now=root;
 72                 while(1)
 73                 {
 74                         pushdown(now);
 75                         if(x<=size[ch[now][0]]) now=ch[now][0];
 76                         else if(x==size[ch[now][0]]+1) break;
 77                         else x-=size[ch[now][0]]+1,now=ch[now][1];
 78                 }
 79                 splay(now,0);
 80                 return now;
 81         }
 82         int calc(int x,int y,int z)
 83         {
 84                 int now=root,rk=size[ch[now][0]]+1,ans=x,fa=0;
 85                 while(now)
 86                 {
 87                         int val=(rk-1)*y+z;
 88                         pushdown(now);
 89                         if(w[now]<val)
 90                         {
 91                                 ans=min(ans,rk);
 92                                 fa=now;
 93                                 now=ch[now][0],rk-=size[ch[now][1]]+1;
 94                         }
 95                         else fa=now,now=ch[now][1],rk+=size[ch[now][0]]+1;
 96                 }
 97                 splay(fa,0);
 98                 return ans;
 99         }
100 }S;
101 signed main()
102 {
103         //freopen("1.in","r",stdin);
104         //freopen("1.out","w",stdout);
105         n=read();
106         for(int i=1;i<=n;i++) f[i].a=read(),f[i].b=read();
107         sort(f+1,f+n+1);
108         S.root=S.cnt=S.size[1]=1;
109         S.w[1]=f[1].b;
110         for(int i=2,L;i<=n;i++)
111         {
112                 L=S.calc(i,f[i].a,f[i].b);
113                 if(L==i)
114                 {
115                         int x=S.kth(L-1);
116                         S.splay(x,0);
117                         S.insert(0,x,f[i].a*(L-1)+f[i].b,1);
118                 }
119                 else
120                 {
121                         int x=L==1?0:S.kth(L-1),y=S.kth(L);
122                         if(x) S.splay(x,0);
123                         S.splay(y,x);
124                         S.w[y]+=f[i].a;
125                         S.lazy[y]+=f[i].a;
126                         S.pushdown(y);
127                         S.insert(x,y,f[i].a*(L-1)+f[i].b,0);
128                 }
129         }
130         for(int i=1;i<=n;i++) g[i]=S.w[S.kth(i)];
131         for(int i=1;i<=n;i++) g[i]+=g[i-1];
132         for(int i=1;i<=n;i++) printf("%lld\n",g[i]);
133         return 0;
134 }

T3

原文地址:https://www.cnblogs.com/AthosD/p/12193079.html

时间: 2024-11-06 09:31:03

省选模拟七 题解的相关文章

省选模拟五 题解

写在前面: 我好菜啊 伯努利数和自然数幂和的式子都能忘 A. 青蛙 标签: 贪心+二分 题解: 首先我们贪心的让尽量多的青蛙免费跳过去,可以二分求出 考虑剩下的青蛙如何让费用最小: 假如免费的青蛙非零,那么一定可以把中间的石头跳完 这种情况下其他每个青蛙的最小花费一定是一次 另一种情况是没有免费的青蛙 直接让花费最小的青蛙把中间的石头跳一遍,其他的花费仍然都为一次 B. 一起自习的日子 标签: 伯努利数 题解: 我们知道伯努利数有两种$B^{-}$和$B^{+}$ 其中: $$\sum\limi

省选模拟6 题解

A. Yist 首先考虑怎样的情况答案是不收敛的. 操作中涉及到对一个权值非$0$,并且不作除法的点的加法贡献. 因为只要最终的答案,可以想到对每个点作为出边的贡献分别处理. 部分分提示求出第一次迭代的贡献,发现对于每个点,贡献都是一个等比数列,所以只要代入求和公式就好了. 然而暴力做的复杂度是$O(mk)$的,会被菊花图的数据卡掉. 考虑如何优化这个东西,根据套路我们将点按照度数分块. 定义度数大于$\sqrt m$的点为重点,度数小于等于$\sqrt m$的点为轻点. 分别讨论: 对于对轻点

省选模拟13 题解

A. 同桌的你 每个人渴望与一个人当同桌. 容易发现这个关系形成内向基环树森林. 问题转化为求基环树森林的最大匹配. 任意选一条环上的边,分别尝试该边为匹配边.非匹配边即可. B. 大水题 一个常用的但想不到的东西:将每种颜色出现次数的差值为定值,转化为对颜色序列差分后相等. 然后暴力的做法是枚举2^8,表示答案出现在指定的颜色集合中,分别将差分数组插入.查询哈希表即可. 下面是一个优化: 容易发现可行的颜色集合对于同一个时刻只有不超过8种. 对于每一个左端点,随着右端点的右移,区间颜色集合改变

省选模拟22 题解

A. 遮天蔽日 学习了计算几何相关的很多新技巧. 求过一点$P$,圆$O$的切线:通过两点距离.半径,用反三角函数可以解得一个角度,然后就可以算了. 求直线$PQ$,与圆$O$的交点:作点$O$关于$PQ$的垂线,通过半径和垂线长度,可以算得垂足与交点的距离,然后就可以算了. 求多边形的重心:任选一点$O$,选择顺时针(逆时针)上相邻的每个点对$(P,Q)$,对横纵坐标分别加权平均,加的权为面积. 公转:使重心旋转,保持每个点与重心的相对位置不变,整个多边形的朝向不变. 因为打的不是正解,所以伪

省选模拟23 题解

这篇题解鸽了. A. Expectation B. Sequence C. Counting 原文地址:https://www.cnblogs.com/skyh/p/12309632.html

省选模拟35 题解

A. two 考虑现在要通过蓝边删掉红边. 其实等价于要找出有哪些红边,满足恰好只有一个端点在蓝边的儿子方向子树中. 考虑对蓝树跑出一个 $dfs$ 序来,那么问题转化为恰好一个端点在给定区间中. 这像是一个二维偏序问题.考虑以线段树下标为其中的第一维,第二维进行排序处理. 然后用一个 $set$ 就可以简单维护了,但是这样做的复杂度是两个 $log$ 的. 考虑一个特殊的操作,开两棵线段树,第一棵以连接两个点中小的 $dfs$ 序为下标,大的为权值,第二棵则相反. 那么问题就转化为取一段前缀或

省选模拟4 题解

A. 点点的圈圈 因为题中保证的特殊性质,容易发现圆之间的关系形成树形结构. 对于每棵子树,选择树的根或者累计所有子树的答案. 问题在于建图,容易发现这个可以用KDTree优化. 考虑将所有的点建在KDTree上. 用每个点的圆覆盖KDTree,当完全覆盖时直接塞入对应点的vector中. 之后DFS一遍KDTree,同时用一个set维护祖先链上所有的vector的集合. set所表示的集合即能覆盖该点的所有的圆. 对于KDTree中每一个点,直接在set中查后继就可以找到他的父亲. B. 点点

省选模拟11 题解

A. 组合数问题 还没想明白如何做,待补. B. recollection 因为原图为trie树,树上两个点的lcp长度等于两个点的lca深度. 考虑通过广义sam来维护两个点的lcs. 树上同时对应着一个$endpos$,树上两个点对应的$endpos$对应的广义sam上节点在后缀树的lca的$len$即为两个点的lcs长度. 所以对于原树上每个点,我们只关注它的子树在后缀树上能形成的$len$最大的lca. 树上$n$个点形成的lca集合,实际上等于dfs序上相邻的两点形成的lca集合. 所

省选模拟10 题解

A. 食物链 在拓扑序上dp. B. 选点游戏 要求支持合并两棵树,并同时维护树上最大独立集. 考虑特殊情况,每次只加一个标号最大的点,问题是一个简单的动态dp. 离线出最终树的形态,考虑加入的一个叶子节点,更新该节点到1号节点的轻链上信息就可以了. 其实想到这里,一个动态dp的做法已经很显然了,但是考试时仍然没有想到. 仍然维护出原树的形态. 每次的操作是合并两个联通块,也就是合并一对父子. 实际上只要将子节点处的信息更新到父节点直到父节点同连通块的祖先路径上的轻链就可以了. 因为每条重链的链