[关闭]
@exut 2024-11-14T02:52:36.000000Z 字数 4019 阅读 143

11.12

刷题


CF915E

第一眼板子,第二眼还是板子

做法可以考虑ODT,但是那样太玄乎了算了吧

考虑动态开点线段树

非常简单好写,但是空间有点申比

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=5e5+5;
  4. int ls[N*33],rs[N*33];
  5. int sum[N*33];
  6. int idx;
  7. int tag[N*33];
  8. void pushup(int rt){
  9. sum[rt]=sum[ls[rt]]+sum[rs[rt]];
  10. }
  11. #define mid (l+r>>1)
  12. void pushdown(int rt,int l,int r){
  13. if(tag[rt]==-1) return;
  14. if(!ls[rt]) ls[rt]=++idx;
  15. if(!rs[rt]) rs[rt]=++idx;
  16. tag[ls[rt]]=tag[rs[rt]]=tag[rt],tag[rt]=-1;
  17. sum[ls[rt]]=(mid-l+1)*tag[ls[rt]];
  18. sum[rs[rt]]=(r-mid)*tag[rs[rt]];
  19. }
  20. void modi(int& rt,int l,int r,int L,int R,bool k){
  21. if(l>R or L>r) return;
  22. if(!rt) rt=++idx;
  23. if(L<=l and r<=R){
  24. tag[rt]=k;
  25. sum[rt]=(r-l+1)*k;
  26. return;
  27. }
  28. pushdown(rt,l,r);
  29. modi(ls[rt],l,mid,L,R,k);
  30. modi(rs[rt],mid+1,r,L,R,k);
  31. pushup(rt);
  32. }
  33. int rot;
  34. int n,q;
  35. int main(){
  36. ios::sync_with_stdio(0);
  37. cin.tie(0),cout.tie(0);
  38. cin>>n>>q;
  39. memset(tag,-1,sizeof tag);
  40. modi(rot,1,n,1,n,1);
  41. while(q--){
  42. int l,r,k;
  43. cin>>l>>r>>k;
  44. if(k==1) modi(rot,1,n,l,r,0);
  45. else modi(rot,1,n,l,r,1);
  46. cout<<sum[rot]<<"\n";
  47. }
  48. }

CF731E

考虑到这个放一个串代替一段前缀的过程没有什么很大的意义其实,直接可以转化成每次取的位置严格单调即可

表示取到 ,是 这个人操作的答案,显然两个人对答案分别作正和负贡献

复杂度 ,考虑优化

注意到转移点都是前面的dp值+前缀和的最大值或者最小,可以开两个线段树优化,但是注意到这里是前缀最值,所以树状数组也是个好的选择

应该可做,但是复杂了一点,尤其边界貌似有点奇怪

考虑到这个人这一维并不重要,我们只在乎得分差,而且边界貌似也是从后往前更好考虑来着(一口气拿完)

我们设 表示从 的最优答案

则有

转移发现后面那一坨始终是一样的,记录最优点惰性转移就行,当然你闲得蛋疼写个树状数组应该也没事就是了

CF865D

经典带悔贪心板子

但是我贪心真的很差,一直不太能理解这种玩意,考贪心那就寄了,悲,甚至贪心比dp还差

CF1439C

某P教练终于找到好玩的题了/kk

感觉比较神秘

这个单调性质很猛,而且修改不会破坏,直接可以考虑上线段树二分,于是操作1是ez的

考虑操作2,直接在线段树上跑,如果一个区间右端点小于x则贡献为0直接返回

如果一个区间的和都小于y就贡献为区间长度,也返回,此时把y减去区间和,然后继续递归

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=2e5+5;
  4. #define int long long
  5. int n,q,a[N];
  6. #define ls (rt<<1)
  7. #define rs (rt<<1|1)
  8. #define mid (l+r>>1)
  9. int sum[N*4],mi[N*4],tag[N*4];
  10. void pushup(int rt){
  11. sum[rt]=sum[ls]+sum[rs];
  12. mi[rt]=min(mi[ls],mi[rs]);
  13. }
  14. void build(int rt,int l,int r){
  15. if(l==r){
  16. sum[rt]=mi[rt]=a[l];
  17. return;
  18. }
  19. build(ls,l,mid);
  20. build(rs,mid+1,r);
  21. pushup(rt);
  22. }
  23. void pushdown(int rt,int l,int r){
  24. if(tag[rt]){
  25. tag[ls]=tag[rs]=tag[rt],tag[rt]=0;
  26. sum[ls]=tag[ls]*(mid-l+1);
  27. mi[ls]=tag[ls];
  28. sum[rs]=tag[rs]*(r-mid);
  29. mi[rs]=tag[rs];
  30. }
  31. }
  32. int query_pos(int rt,int l,int r,int val){
  33. if(l==r){
  34. if(val>mi[rt])return l;
  35. return l+1;
  36. }
  37. pushdown(rt,l,r);
  38. if(mi[ls]>=val) return query_pos(rs,mid+1,r,val);
  39. return query_pos(ls,l,mid,val);
  40. }
  41. int query(int rt,int l,int r,int L,int& y){
  42. if(y<mi[rt] or r<L) return 0;
  43. if(l>=L and y>=sum[rt]){
  44. y-=sum[rt];
  45. return min(n,r)-l+1;
  46. }
  47. pushdown(rt,l,r);
  48. return query(ls,l,mid,L,y)+query(rs,mid+1,r,L,y);
  49. }
  50. void modi(int rt,int l,int r,int L,int R,int k){
  51. if(L<=l and r<=R){
  52. sum[rt]=(r-l+1)*k;
  53. mi[rt]=k;
  54. tag[rt]=k;
  55. return;
  56. }
  57. if(L>r or l>R) return;
  58. pushdown(rt,l,r);
  59. modi(ls,l,mid,L,R,k);
  60. modi(rs,mid+1,r,L,R,k);
  61. pushup(rt);
  62. }
  63. signed main(){
  64. ios::sync_with_stdio(0);
  65. cin.tie(0),cout.tie(0);
  66. cin>>n>>q;
  67. for(int i=1;i<=n;i++) cin>>a[i];
  68. build(1,1,n);
  69. while(q--){
  70. int op,x,y;
  71. cin>>op>>x>>y;
  72. if(op==1){
  73. int l=query_pos(1,1,n,y);
  74. if(l<=x) modi(1,1,n,l,x,y);
  75. }
  76. else cout<<query(1,1,n,x,y)<<"\n";
  77. }
  78. }

CF893E

首先质因数分解,假设出现过的次幂依次为

正负重要吗?不重要,只需要最后乘上 即可,也就是前面任选最后一个来补上位置

对于每个质因子单独考虑,插板可得方案数为 ,质因数分解然后乘起来就好了

CF388C

永远会讨厌贪心的,但是这题还简单

猜一下结论,先手开始拿一堆之后,必将会先后手交替拿这一堆拿完位置

然后当前操作手拿剩下能摸到的最大的那个所在的那一堆

看着不太对,但是手玩样例都过了,怪,写一下看看

好的,WA on test 12,我就知道

感觉问题出在当前活动手拿哪一堆上面,只是拿最大值所在堆有点太肤浅了

我们考虑到偶数堆谁先手谁后手是无所谓的,而且也不会交换先后手

对于奇数堆,这才是需要考虑的,现在考虑每次拿收益最大的堆而不是最大值的堆

然后又寄了,这次WA on test 6

然后我们考虑到按我们的贪心,前半段和后半段貌似不是需要考虑的东西,对于奇数堆来说有影响的貌似只是最中间那一个牌而已,于是先处理偶数的堆,然后剩下的按中间那张牌大小互相贪心拿

过了

我不会贪心,但是不妨碍我能猜/kk

吐槽一下这道题样例真的非常弱小,根本测不出任何问题,但是实际上上面两种错误写法都非常好hack

值得一提的是本题的数据范围太小了,其实完全可以加大

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n;
  4. vector<int> s[105];
  5. bool cut[105];
  6. int main(){
  7. ios::sync_with_stdio(0);
  8. cin.tie(0),cout.tie(0);
  9. cin>>n;
  10. for(int i=1;i<=n;i++){
  11. int wc;
  12. cin>>wc;
  13. for(int j=1;j<=wc;j++){
  14. int x;
  15. cin>>x;
  16. s[i].push_back(x);
  17. }
  18. }
  19. int ans1=0,ans2=0;
  20. bool di=1;//谁操
  21. int pos=0;
  22. for(int i=1;i<=n;i++){
  23. if(s[i].size()%2==0){
  24. cut[i]=1;
  25. int l=0,r=s[i].size()-1;
  26. while(l<=r){
  27. if(di) ans1+=s[i][l++],di^=1;
  28. else ans2+=s[i][r--],di^=1;
  29. }
  30. }
  31. }
  32. while(1){
  33. int mx=-1;
  34. pos=0;
  35. for(int i=1;i<=n;i++){
  36. if(cut[i]) continue;
  37. if(mx<s[i][s[i].size()/2]) pos=i,mx=s[i][s[i].size()/2];
  38. }
  39. if(!pos){
  40. cout<<ans1<<" "<<ans2;
  41. return 0;
  42. }
  43. cut[pos]=1;
  44. int l=0,r=s[pos].size()-1;
  45. while(l<=r){
  46. if(di) ans1+=s[pos][l++],di^=1;
  47. else ans2+=s[pos][r--],di^=1;
  48. }
  49. }
  50. return 0;
  51. }

存在实现难度但是说实话不高真的

收工,疑似首次把题单做完,喜

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注