当前位置:   article > 正文

24.5.26(树链剖分板子,二分+线段树)

24.5.26(树链剖分板子,二分+线段树)

星期一:

补重庆科技 C 二分                                               牛客传送门

思路:二维前缀和表示到第 i个人第 j个弹巢开了多少发,和st【i】表示第 i个人开的是第几个弹巢

对于 l和r的查询,使用前缀和二分找出第一个中枪的人,但因为题意第 l个人开的是1号弹巢,所以弹巢编号会有一个偏移,例如st【l】==5,那么偏移量就为4

这个偏移量在check中的使用也卡了我好一会儿,但感觉是我的问题,其实挺简单。例如py==4,查询 1号弹巢时,实际上应该看sum中的 5号弹巢有无射击,因为预处理中 l开的是 5号弹巢,实际开的是 1号,从 l到 r的所有人都会存在这个偏移

代码如下:

  1. const int N=2e6+10,M=210;
  2. const ll INF=0x3f3f3f3f3f3f3f3f;
  3. const int mod=1e9+7;
  4. ll n;
  5. int m;
  6. int b[N],a[N];
  7. int sum[N][22],st[N];
  8. int py,tl;
  9. bool mp[22];
  10. bool check(int x){
  11. for(int i=1;i<=m;i++){
  12. int j=(i+py-1)%m+1; //偏移量的处理
  13. if(mp[i] && sum[x][j]-sum[tl][j]) return 1; //有人出子弹了
  14. }
  15. return 0;
  16. }
  17. void solve(){
  18. int q; cin >> m >> n >> q;
  19. for(int i=1;i<=m;i++){
  20. cin >> b[i];
  21. mp[i]=b[i];
  22. }
  23. for(int i=1;i<=n;i++){
  24. cin >> a[i];
  25. a[i]%=m;
  26. }
  27. int box=1;
  28. sum[1][1]=1,st[1]=1;
  29. for(int i=2;i<=n;i++){
  30. box=(box+a[i-1]-1)%m+1;
  31. st[i]=box;
  32. for(int j=1;j<=m;j++) sum[i][j]=sum[i-1][j];
  33. sum[i][box]++;
  34. }
  35. while(q--){
  36. int l,r; cin >> l >> r;
  37. py=st[l]-1; //弹匣偏移量
  38. int res=0; tl=l-1;
  39. while(l<=r){
  40. int mid=l+r>>1;
  41. if(check(mid)) res=mid,r=mid-1;
  42. else l=mid+1;
  43. }
  44. if(!res) cout << "No one died\n";
  45. else cout << res << "\n";
  46. }
  47. }

武汉纺织 A                                                           牛客传送门

赛时没出我的,第一重循环优化了下加了个continue,把自己坑了,不加continue两重循环足够了,加了continue后还得第三重循环枚举公约数,但当时光想着continue能剪枝

思路:第一重循环从m开始枚举公约数,第二重循环算 k,算出来的数的gcd不一定就是 i,开个变量gc去维护一下其gcd

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. int b[N];
  4. bool cmp1(PII a,PII b){
  5. if(a.first!=b.first) return a.first>b.first;
  6. else return a.second<b.second;
  7. }
  8. void solve(){
  9. int m; cin >> n >> m;
  10. int ma=0;
  11. for(int i=1;i<=n;i++){
  12. int x; cin >> x;
  13. b[x]++;
  14. ma=max(x,ma);
  15. }
  16. vector<PII>ve;
  17. for(int i=m;i<=ma;i++){
  18. int t=0,gc=0;
  19. for(int j=i;j<=ma;j+=i){
  20. if(b[j]){
  21. t+=b[j];
  22. gc=__gcd(j,gc);
  23. }
  24. }
  25. if(t>1) ve.push_back({t,gc});
  26. }
  27. sort(ve.begin(),ve.end(),cmp1);
  28. if(!ve.empty()) cout << ve[0].first << " " << ve[0].second << "\n";
  29. else cout << "0 0\n";
  30. }

补cf 945 div2 C 构造                                                 cf传送门

题意:给一个排列p,输出一个排列q,设a【i】= p【i】+ q【i】,使存在最多 a【i】大于两侧值

思路:可看出峰值的数量是固定的,为 n/2-1,头尾不可为峰,连续俩数不能都为峰,假如我们让奇数位置的值为峰( 除了头,发现如果p的 n值在偶数位,且挨着1,是无法实现的,但其实也不难解决,将p翻转后 n下标的奇偶性就会改变( 最后输出前记得翻转 q

解决了n的问题后就很好操作了,我们把 1 - n/2的值给偶数位,n/2+1 - n的给奇数位,越小的p加上越大的q,这样偶数位最高为 n,奇数位最少为 n+1,即满足题意

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. int p[N],pos[N],q[N];
  4. void solve(){
  5. cin >> n;
  6. for(int i=1;i<=n;i++){
  7. cin >> p[i];
  8. pos[p[i]]=i;
  9. }
  10. bool ifr=0;
  11. if(!(pos[n]&1)){ //n在偶数位,将其翻转
  12. ifr=1;
  13. reverse(p+1,p+n+1);
  14. for(int i=1;i<=n;i++) pos[p[i]]=i;
  15. }
  16. vector<int>od,ev;
  17. for(int i=n;i;i--)
  18. i<=n/2?ev.push_back(i):od.push_back(i);
  19. int ido=0,ide=0;
  20. for(int i=1;i<=n;i++){
  21. if(pos[i]&1) q[pos[i]]=od[ido++];
  22. else q[pos[i]]=ev[ide++];
  23. }
  24. if(ifr) reverse(q+1,q+n+1); //记得判断是否翻转过p
  25. for(int i=1;i<=n;i++) cout << q[i] << " \n"[i==n];
  26. }

dp题单 换根dp第三题                                             cf传送门

换根板子题,10min出了,不多讲

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. ll a[N],sum[N],dp[N],tot;
  4. int dep[N];
  5. vector<int>ve[N];
  6. void dfs1(int x,int fa){
  7. if(fa) dep[x]=dep[fa]+1;
  8. sum[x]=a[x];
  9. for(int i:ve[x]){
  10. if(i==fa) continue;
  11. dfs1(i,x);
  12. sum[x]+=sum[i];
  13. }
  14. }
  15. void dfs2(int x,int fa){
  16. for(int i:ve[x]){
  17. if(i==fa) continue;
  18. dp[i]=dp[x]-sum[i]+tot-sum[i]; //i为根子树距离减一,其余所有节点距离加一
  19. dfs2(i,x);
  20. }
  21. }
  22. void solve(){
  23. cin >> n;
  24. for(int i=1;i<=n;i++){
  25. cin >> a[i];
  26. tot+=a[i];
  27. }
  28. for(int i=1;i<n;i++){
  29. int u,v; cin >> u >> v;
  30. ve[u].push_back(v);
  31. ve[v].push_back(u);
  32. }
  33. dfs1(1,0);
  34. for(int i=1;i<=n;i++) dp[1]+=1ll*dep[i]*a[i];
  35. dfs2(1,0);
  36. ll ans=0;
  37. for(int i=1;i<=n;i++) ans=max(dp[i],ans);
  38. cout << ans;
  39. }

星期二:

下午19届黑龙江,止步5题

补 F                                                                         cf传送门

题意:在图上找一条包含点数不超过5的简单路径,输出点集最大权值和

思路:路径长度不超过5,可以把长度为3的路径存下来,然后遍历两头的点往两边延伸,遍历只需要遍历权值前四的点,因为极端情况下,端点找出去最多三个点可能重复,为中间点,另一端点,另一端点延伸的点,所以前四一定可以覆盖最优情况

代码如下:

  1. ll n;
  2. ll a[5050];
  3. vector<int>ve[5050];
  4. struct nod{
  5. int p1,p2,p;
  6. };
  7. vector<nod>tri;
  8. bool cmp1(int x,int y){
  9. return a[x]>a[y];
  10. }
  11. void solve(){
  12. int m; cin >> n >> m;
  13. for(int i=1;i<=n;i++) cin >> a[i];
  14. ll ans=0;
  15. for(int i=1;i<=m;i++){
  16. int u,v; cin >> u >> v;
  17. ve[u].push_back(v);
  18. ve[v].push_back(u);
  19. ans=max(a[u]+a[v],ans);
  20. }
  21. for(int i=1;i<=n;i++){
  22. for(auto p1:ve[i]){
  23. for(auto p2:ve[i])
  24. if(p1!=p2) tri.push_back({p1,p2,i});
  25. }
  26. }
  27. for(int i=1;i<=n;i++) sort(ve[i].begin(),ve[i].end(),cmp1);
  28. for(auto t:tri){
  29. int p1=t.p1,p2=t.p2,p=t.p;
  30. if(p1==p2 || p1==p || p2==p) continue;
  31. ll sum=a[p1]+a[p2]+a[p];
  32. ans=max(sum,ans);
  33. for(int i=0,sz1=ve[p1].size();i<min(sz1,4);i++){
  34. for(int j=0,sz2=ve[p2].size();j<min(sz2,4);j++){
  35. int to1=ve[p1][i],to2=ve[p2][j];
  36. if(to1==p2 || to2==p1) continue;
  37. if(to1==p || to2==p || to1==to2) continue;
  38. ans=max(sum+a[to1]+a[to2],ans);
  39. }
  40. }
  41. for(int i=0,sz=ve[p1].size();i<min(sz,4);i++){
  42. int to=ve[p1][i];
  43. if(to==p || to==p2) continue;
  44. ans=max(sum+a[to],ans);
  45. }
  46. for(int i=0,sz=ve[p2].size();i<min(sz,4);i++){
  47. int to=ve[p2][i];
  48. if(to==p || to==p1) continue;
  49. ans=max(sum+a[to],ans);
  50. }
  51. }
  52. cout << ans;
  53. }

星期三:

补19届黑龙江 C   树链剖分

补题失败,先记着

星期四:

早上和os vp了24江苏,4题298,铜首,少wa两发就是银尾,总结下经验

24江苏 G(吃了3发罚时                                             cf传送门

大量的大整数输入尽量不要用double存,会很慢,因此 t了两发

整数没有确定不会爆 int就开 ll,double改成 int后 wa了一发

os写二分check的细节失误,wa了一发

后两发是完全可以避免的

       !!!!!!!!!!!!!!!!警钟长鸣!!!!!!!!!!!!!!!!!!

24江苏  K  博弈                                                        cf传送门

思路:首先能想到如果只有1,那么1的奇偶性决定胜负,但如果存在2,那么最后一个删2的人就能决定1的奇偶性,即决定谁胜。那如果存在3呢,那么最后一个删3的人就能决定2的奇偶性,即决定谁能最后删2,即决定谁胜。   推导到此即可大胆猜出结论,胜负由最大数的奇偶性决定

银牌题就是主席树,线段树也写了,确实倒腾不出来

树链剖分板子题(附线段树区修,区查板子                    洛谷传送门

思路:树链剖分+线段树维护                            推荐教学视频( 阿b传送门

代码如下:

  1. const int N=2e6+10,M=210;
  2. const int mod=1e9+7;
  3. ll n;
  4. int fa[N],dep[N],top[N],sz[N],son[N],dfn[N],tim;
  5. ll v[N];
  6. int m,r,p;
  7. vector<int>ve[N];
  8. struct seg_Tree{
  9. #define lc p<<1
  10. #define rc p<<1|1
  11. struct nod{
  12. int l,r;
  13. ll sum,add;
  14. }t[N];
  15. ll ql,qr,a[N];
  16. nod merge(nod a,nod b){
  17. nod res;
  18. res.l=a.l,res.r=b.r;
  19. res.sum=a.sum+b.sum;
  20. res.add=0;
  21. return res;
  22. }
  23. void pushup(int p){t[p]=merge(t[lc],t[rc]);}
  24. void pushdn(int p){
  25. if(!t[p].add) return ;
  26. t[lc].sum+=(t[lc].r-t[lc].l+1)*t[p].add;
  27. t[rc].sum+=(t[rc].r-t[rc].l+1)*t[p].add;
  28. t[lc].add+=t[p].add;
  29. t[rc].add+=t[p].add;
  30. t[p].add=0;
  31. }
  32. void bd(int p,int l,int r){
  33. t[p]={l,r,0,0};
  34. if(l==r){
  35. t[p].sum=a[l];
  36. return ;
  37. }
  38. int m=l+r>>1;
  39. bd(lc,l,m);
  40. bd(rc,m+1,r);
  41. pushup(p);
  42. }
  43. void update(int p,int v){
  44. if(ql<=t[p].l && qr>=t[p].r){
  45. t[p].sum+=1ll*(t[p].r-t[p].l+1)*v;
  46. t[p].add+=v;
  47. return ;
  48. }
  49. int m=t[p].l+t[p].r>>1;
  50. pushdn(p);
  51. if(ql<=m) update(lc,v);
  52. if(qr>m) update(rc,v);
  53. pushup(p);
  54. }
  55. nod query(int p){
  56. if(ql<=t[p].l && qr>=t[p].r) return t[p];
  57. int m=t[p].l+t[p].r>>1;
  58. pushdn(p);
  59. if(ql>m) return query(rc);
  60. if(qr<=m) return query(lc);
  61. return merge(query(lc),query(rc));
  62. }
  63. void updt(int l,int r,int v){
  64. ql=l;
  65. qr=r;
  66. // qop=op;
  67. update(1,v);
  68. }
  69. ll ask(int l,int r){
  70. ql=l,qr=r;
  71. return query(1).sum;
  72. }
  73. #undef lc
  74. #undef rc
  75. }tr;
  76. void dfs1(int x,int f){
  77. fa[x]=f;
  78. dep[x]=dep[f]+1;
  79. sz[x]=1;
  80. int ts=0;
  81. for(int i:ve[x]){
  82. if(i==f) continue;
  83. dfs1(i,x);
  84. sz[x]+=sz[i];
  85. if(sz[i]>ts) ts=sz[i],son[x]=i;
  86. }
  87. }
  88. void dfs2(int x,int t){
  89. dfn[x]=++tim;
  90. tr.a[tim]=v[x];
  91. top[x]=t;
  92. if(!son[x]) return ;
  93. dfs2(son[x],t);
  94. for(int i:ve[x]){
  95. if(i==son[x] || i==fa[x]) continue;
  96. dfs2(i,i); //注意dfs2的第二个参数也是i
  97. }
  98. }
  99. void update(int x,int y,int z){
  100. z%=p;
  101. while(top[x]!=top[y]){
  102. if(dep[top[x]]<dep[top[y]]) swap(x,y);
  103. tr.updt(dfn[top[x]],dfn[x],z);
  104. x=fa[top[x]];
  105. }
  106. if(dep[x]>dep[y]) swap(x,y);
  107. tr.updt(dfn[x],dfn[y],z);
  108. }
  109. ll query(int x,int y){
  110. ll res=0;
  111. while(top[x]!=top[y]){
  112. if(dep[top[x]]<dep[top[y]]) swap(x,y);
  113. res+=tr.ask(dfn[top[x]],dfn[x]),res%=p;
  114. x=fa[top[x]];
  115. }
  116. if(dep[x]>dep[y]) swap(x,y);
  117. res+=tr.ask(dfn[x],dfn[y]);
  118. return res%p;
  119. }
  120. void solve(){
  121. cin >> n >> m >> r >> p;
  122. for(int i=1;i<=n;i++) cin >> v[i];
  123. for(int i=1;i<n;i++){
  124. int u,v; cin >> u >> v;
  125. ve[u].push_back(v);
  126. ve[v].push_back(u);
  127. }
  128. dfs1(r,r);
  129. dfs2(r,r);
  130. tr.bd(1,1,n);
  131. while(m--){
  132. int op,x; cin >> op >> x;
  133. if(op==1){
  134. int y,z; cin >> y >> z;
  135. update(x,y,z);
  136. }else if(op==2){
  137. int y; cin >> y;
  138. cout << query(x,y) << "\n";
  139. }else if(op==3){
  140. int z; cin >> z;
  141. tr.updt(dfn[x],dfn[x]+sz[x]-1,z);
  142. }else cout << tr.ask(dfn[x],dfn[x]+sz[x]-1)%p << "\n"; //记得取模
  143. }
  144. }

晚上vp了下24长春邀请赛,出了3题就做不动了,很累。。

补ABC292   Ex                                                          atc传送门

思路:线段树维护最大前缀和,然后二分,这是 O(n*(logn)^2)的做法

如果没有B这个条件,那么我们只需要知道得分总和,rating即为sum/n

但存在B的条件后,我们需要知道第一个rating>=B的点,对式子进行如下演化

我们可以对于 pi-B的值做一个前缀和,然后找到第一个前缀和>=0的点即可

但是问题是,前缀和并不具有单调性,如何才能快速找到第一个点呢?

这里要引进线段树的一个新功能,维护最大前缀和,which具有单调性,即可进行二分查询

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. ll b;
  4. struct seg_Tree{
  5. #define lc p<<1
  6. #define rc p<<1|1
  7. struct nod{
  8. int l,r;
  9. ll sum,pre;
  10. }t[N];
  11. int ql,qr; //查询区间
  12. nod merge(nod a,nod b){
  13. nod res;
  14. res.l=a.l,res.r=b.r;
  15. res.sum=a.sum+b.sum;;
  16. res.pre=max(a.sum+b.pre,a.pre);
  17. return res;
  18. }
  19. void pushup(int p){t[p]=merge(t[lc],t[rc]);} //向上更新
  20. void bd(int p,int l,int r){ //bd里处理输入
  21. if(l==r){
  22. t[p]={l,r,0,0};
  23. cin >> t[p].sum;
  24. t[p].sum-=b;
  25. t[p].pre=t[p].sum;
  26. return ;
  27. }
  28. int m=l+r>>1;
  29. bd(lc,l,m);
  30. bd(rc,m+1,r);
  31. pushup(p);
  32. }
  33. void update(int p,int v){
  34. if(ql<=t[p].l && qr>=t[p].r){
  35. t[p].sum=v-b; //叶子节点的所有信息都要改
  36. t[p].pre=t[p].sum;
  37. return ;
  38. }
  39. int m=t[p].l+t[p].r>>1;
  40. if(ql<=m) update(lc,v);
  41. if(qr>m) update(rc,v);
  42. pushup(p); //向上更新
  43. }
  44. nod query(int p){
  45. if(ql<=t[p].l && qr>=t[p].r) return t[p];
  46. int m=t[p].l+t[p].r>>1;
  47. if(ql>m) return query(rc);
  48. if(qr<=m) return query(lc);
  49. return merge(query(lc),query(rc));
  50. }
  51. void updt(int l,int r,int v){
  52. ql=l;
  53. qr=r;
  54. // qop=op;
  55. update(1,v);
  56. }
  57. ll ask_pre(int l,int r){
  58. ql=l,qr=r;
  59. return query(1).pre;
  60. }
  61. ll ask_sum(int l,int r){
  62. ql=l,qr=r;
  63. return query(1).sum;
  64. }
  65. #undef lc
  66. #undef rc
  67. }tr;
  68. void solve(){
  69. cout << fixed << setprecision(13);
  70. int q; cin >> n >> b >> q;
  71. tr.bd(1,1,n);
  72. while(q--){
  73. int c,x; cin >> c >> x;
  74. tr.updt(c,c,x);
  75. int l=1,r=n,res=n;
  76. while(l<=r){ //二分找到第一个pre>=0的点
  77. int mid=l+r>>1;
  78. if(tr.ask_pre(1,mid)>=0) res=mid,r=mid-1;
  79. else l=mid+1;
  80. }
  81. cout << 1.0*(tr.ask_sum(1,res)+1ll*res*b)/res << "\n";
  82. }
  83. }

星期五:

补24长春邀请赛 B 树形dp                                   cf传送门

题意:找出一种dfs的方式,使得dfs序中偶数下标节点的权值和最大

思路:很明显的树形dp,但赛时因脑力不足只定了个状态,没有更细想如何转移

dp【i】【0/1】表示以偶数/奇数顺序进入 i节点的最大答案,dp【1】【1】为答案

偶数sz的子节点,不会影响奇偶顺序,一个奇子节点会影响一次

如果只有偶数子节点,那么0/1的选择是固定的,如果存在奇节点,所有偶节点可以取更大值,对于奇节点的选择,如果偶数个奇节点,0/1各一半,如果奇数个,多的那个取父节点的反,将奇点的 0/1 差值排个序,贪心即可

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. ll dp[N][2],a[N];
  4. int sz[N];
  5. vector<int>ve[N];
  6. bool cmp1(int a,int b){
  7. return dp[a][0]-dp[a][1]>dp[b][0]-dp[b][1];
  8. }
  9. void dfs(int x,int f){
  10. dp[x][0]=a[x];
  11. dp[x][1]=0;
  12. sz[x]=1;
  13. bool if1=0;
  14. for(int i:ve[x]){
  15. if(i==f) continue;
  16. dfs(i,x);
  17. sz[x]+=sz[i];
  18. if(sz[i]&1) if1=1;
  19. }
  20. if(!if1){
  21. for(int i:ve[x]){
  22. if(i==f) continue;
  23. dp[x][1]+=dp[i][0];
  24. dp[x][0]+=dp[i][1];
  25. }
  26. }else{
  27. vector<int>son;
  28. for(int i:ve[x]){
  29. if(i==f) continue;
  30. if(sz[i]&1) son.push_back(i);
  31. else{
  32. dp[x][1]+=max(dp[i][0],dp[i][1]);
  33. dp[x][0]+=max(dp[i][0],dp[i][1]);
  34. }
  35. }
  36. sort(son.begin(),son.end(),cmp1);
  37. if(son.size()&1){
  38. for(int i=0,ssz=son.size();i<ssz;i++){
  39. if(i<ssz/2) dp[x][0]+=dp[son[i]][0],dp[x][1]+=dp[son[i]][0];
  40. else if(i==ssz/2) dp[x][0]+=dp[son[i]][1],dp[x][1]+=dp[son[i]][0];
  41. else dp[x][0]+=dp[son[i]][1],dp[x][1]+=dp[son[i]][1];
  42. }
  43. }else{
  44. for(int i=0,ssz=son.size();i<ssz;i++){
  45. if(i<ssz/2) dp[x][0]+=dp[son[i]][0],dp[x][1]+=dp[son[i]][0];
  46. else dp[x][0]+=dp[son[i]][1],dp[x][1]+=dp[son[i]][1];
  47. }
  48. }
  49. }
  50. }
  51. void solve(){
  52. cin >> n;
  53. for(int i=1;i<=n;i++) ve[i].clear();
  54. for(int i=1;i<=n;i++) cin >> a[i];
  55. for(int i=1;i<n;i++){
  56. int u,v; cin >> u >> v;
  57. ve[u].push_back(v);
  58. ve[v].push_back(u);
  59. }
  60. dfs(1,0);
  61. cout << dp[1][1] << "\n";
  62. }

easy线段树题 建平台                                               洛谷传送门

思路:线段树维护区间高度,需支持区间修改和单点查询

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. struct seg_Tree{
  4. #define lc p<<1
  5. #define rc p<<1|1
  6. struct nod{
  7. int l,r;
  8. int hei,add;
  9. }t[N];
  10. int ql,qr,qv;
  11. nod merge(nod a,nod b){
  12. nod res;
  13. res.l=a.l,res.r=b.r;
  14. res.hei=max(a.hei,b.hei); // useless,因为查询高度只会是单点
  15. res.add=0;
  16. return res;
  17. }
  18. void pushup(int p){t[p]=merge(t[lc],t[rc]);}
  19. void pushdn(int p){
  20. if(!t[p].add) return ;
  21. t[lc].hei=t[p].add;
  22. t[rc].hei=t[p].add;
  23. t[lc].add=t[p].add;
  24. t[rc].add=t[p].add;
  25. t[p].add=0;
  26. }
  27. void bd(int p,int l,int r){
  28. t[p]={l,r,0,0};
  29. if(l==r) return ;
  30. int m=l+r>>1;
  31. bd(lc,l,m);
  32. bd(rc,m+1,r);
  33. pushup(p);
  34. }
  35. void update(int p){
  36. if(ql<=t[p].l && qr>=t[p].r){
  37. t[p].hei=qv;
  38. t[p].add=qv;
  39. return ;
  40. }
  41. int m=t[p].l+t[p].r>>1;
  42. pushdn(p);
  43. if(ql<=m) update(lc);
  44. if(qr>m) update(rc);
  45. pushup(p);
  46. }
  47. nod query(int p){
  48. if(ql<=t[p].l && qr>=t[p].r) return t[p];
  49. int m=t[p].l+t[p].r>>1;
  50. pushdn(p);
  51. if(qr<=m) return query(lc);
  52. if(ql>m) return query(rc);
  53. return merge(query(lc),query(rc));
  54. }
  55. void updt(int l,int r,int v){
  56. ql=l,qr=r;
  57. qv=v;
  58. update(1);
  59. }
  60. int ask_hei(int l,int r){
  61. ql=l,qr=r;
  62. return query(1).hei;
  63. }
  64. #undef lc
  65. #undef rc
  66. }tr;
  67. struct nod{
  68. int l,r,y;
  69. }bo[110];
  70. void solve(){
  71. cin >> n;
  72. int ma=0;
  73. for(int i=1;i<=n;i++){
  74. cin >> bo[i].y >> bo[i].l >> bo[i].r;
  75. bo[i].l++;
  76. ma=max(bo[i].r,ma);
  77. }
  78. tr.bd(1,1,ma);
  79. sort(bo+1,bo+n+1,[](nod a,nod b){return a.y<b.y;}); //按高度排序
  80. ll ans=0;
  81. for(int i=1;i<=n;i++){
  82. int h=bo[i].y;
  83. ans+=h-tr.ask_hei(bo[i].l,bo[i].l)+h-tr.ask_hei(bo[i].r,bo[i].r);
  84. tr.updt(bo[i].l,bo[i].r,h);
  85. }
  86. cout << ans;
  87. }

dp题单 状压dp第五题 鱼吃鱼                              cf传送门

题意:n条鱼,每次两条鱼相遇且有一条被对方吃掉,n*n矩阵 aij为 i鱼吃掉 j鱼概率,问每条鱼活到最后的期望是多少

思路:dp【mask】表示状态为mask的期望,先设定全1期望为1,然后倒着枚举状态,对于每一个状态,先枚举被吃掉的0,再枚举吃掉它的1

dp【mask】+= 原状态期望*两鱼相遇概率* j吃掉 i的概率

代码如下:

  1. ll n;
  2. double dp[1<<19];
  3. double a[19][19];
  4. ll c(int n,int m){
  5. ll res=1;
  6. for(int i=1;i<=m;i++)
  7. res=res*(i+n-m)/i;
  8. return res;
  9. }
  10. void solve(){
  11. cout << fixed << setprecision(8);
  12. cin >> n;
  13. for(int i=0;i<n;i++){
  14. for(int j=0;j<n;j++) cin >> a[i][j];
  15. }
  16. dp[(1<<n)-1]=1;
  17. for(int mask=(1<<n)-1;mask;mask--){ //状态倒着枚举
  18. for(int i=0;i<n;i++){
  19. if(mask>>i&1) continue;
  20. for(int j=0;j<n;j++){
  21. if(!(mask>>j&1)) continue;
  22. int nmask=mask|(1<<i); //i被吃掉前的状态
  23. dp[mask]+=dp[nmask]*1.0/c(__builtin_popcount(nmask),2)*a[j][i];
  24. }
  25. }
  26. }
  27. for(int i=0;i<n;i++) cout << dp[1<<i] << " ";
  28. }

星期六:

线段树区间乘区间加区间查询 模板                                       洛谷传送门

思路:需要两个懒标记,一个负责区间加,一个负责区间乘,注意mul对add的影响

对于存在add的区间再进行乘操作后,区间应是(sum+len*add)*k,分配后为sum*k+len*add*k,所以不仅mul*=k,add也需*=k,pushdn时先处理mul再add(因为add已经乘过了

代码如下:

  1. const int N=2e6+10,M=210;
  2. ll n;
  3. int m;
  4. struct seg_Tree{
  5. #define lc p<<1
  6. #define rc p<<1|1
  7. struct nod{
  8. int l,r;
  9. ll sum,add,mul; //区间和,加法懒标记,乘法懒标记
  10. }t[N];
  11. ll ql,qr,qv,qop;
  12. nod merge(nod a,nod b){
  13. nod res;
  14. res.l=a.l,res.r=b.r;
  15. res.sum=a.sum+b.sum;
  16. res.add=0;
  17. res.mul=1;
  18. return res;
  19. }
  20. void pushup(int p){t[p]=merge(t[lc],t[rc]);}
  21. void pushdn(int p){ //注意先乘再加
  22. t[lc].sum*=t[p].mul,t[lc].sum+=(t[lc].r-t[lc].l+1)*t[p].add,t[lc].sum%=m;
  23. t[rc].sum*=t[p].mul,t[rc].sum+=(t[rc].r-t[rc].l+1)*t[p].add,t[rc].sum%=m;
  24. t[lc].add*=t[p].mul,t[lc].add+=t[p].add,t[lc].add%=m;
  25. t[rc].add*=t[p].mul,t[rc].add+=t[p].add,t[rc].add%=m;
  26. t[lc].mul*=t[p].mul,t[lc].mul%=m;
  27. t[rc].mul*=t[p].mul,t[rc].mul%=m;
  28. t[p].add=0,t[p].mul=1;
  29. }
  30. void bd(int p,int l,int r){
  31. t[p]={l,r,0,0,1};
  32. if(l==r){
  33. cin >> t[p].sum;
  34. return ;
  35. }
  36. int m=l+r>>1;
  37. bd(lc,l,m);
  38. bd(rc,m+1,r);
  39. pushup(p);
  40. }
  41. void update(int p){
  42. if(ql<=t[p].l && qr>=t[p].r){
  43. if(qop==1){
  44. t[p].sum*=qv,t[p].sum%=m;
  45. t[p].mul*=qv,t[p].mul%=m;
  46. t[p].add*=qv,t[p].add%=m;
  47. }else{
  48. t[p].sum+=1ll*(t[p].r-t[p].l+1)*qv,t[p].sum%=m;
  49. t[p].add+=qv,t[p].add%=m;
  50. }
  51. return ;
  52. }
  53. int mid=t[p].l+t[p].r>>1;
  54. pushdn(p);
  55. if(ql<=mid) update(lc);
  56. if(qr>mid) update(rc);
  57. pushup(p);
  58. }
  59. nod query(int p){
  60. if(ql<=t[p].l && qr>=t[p].r) return t[p];
  61. int m=t[p].l+t[p].r>>1;
  62. pushdn(p);
  63. if(ql>m) return query(rc);
  64. if(qr<=m) return query(lc);
  65. return merge(query(lc),query(rc));
  66. }
  67. void updt(int l,int r,int op,int v){
  68. ql=l;
  69. qr=r;
  70. qv=v;
  71. qop=op;
  72. update(1);
  73. }
  74. ll ask(int l,int r){
  75. ql=l,qr=r;
  76. return query(1).sum;
  77. }
  78. #undef lc
  79. #undef rc
  80. }tr;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/655019
推荐阅读
相关标签
  

闽ICP备14008679号