4.5-4.10周记 712 div2 A-D,POJ-2528,线段树模板

离散化+区间覆盖
离散化其实就是把很大的数据范围映射到它们在数组中对应的位置
但是这道题比较特别,因为在输入的左右值不是海报区间的左右端点而是左右的块,导致在覆盖的时候如果一张海报(c)的最前面的单元块的编号比在它前面的某个海报(b)的最后一个单元块多2,(网上都说是大于1但是其实直接等于2可以省掉好多空间)在这里插入图片描述
在这里插入图片描述
我们在映射的时候获得的新数组是[1,2],[2,3],[3,4]对应原来的[1,4],[4,6],[6,9],就会导致这两张海报把它们前面的卡在b开头c结尾的海报( a )覆盖。所以就不要那么拮据,如果两张海报之间相差2,我们直接在它们中间再插入一个数,让它卡着bc中间的那个位置,相当于在bc之间挤出一条缝让a不会被覆盖,这个挤出来的位置可以在后面lower_bound()排序找位置的时候起作用。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
/*调了3个小时 首先是符号错误 一个等于号写成加号 一直死循环 以后在最可能出错的地方
 * 随便弄个cout<<"sb"<<endl;(如果实在不知道 真的可以在程序里二分查找bug 比较确定的话直接在最可能出错的函数里面输出 或者是可以养成
 * 在打题的时候就先输出最不确定的值 (下次试试看)
 * */
const int N=(10000<<1)+7;
struct node{
    int l ,r,val;
}tr[N<<4];//在不超过范围内尽量开大吧
int vis[N],L[N],R[N],a[N<<2],ans;
void build(int k,int l,int r){
    tr[k].l=l,tr[k].r=r;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
void push_down(int k){
    if(tr[k].val){
        tr[k<<1].val=tr[k].val;tr[k<<1|1].val=tr[k].val;
        tr[k].val=0;//下传之后应该置零 不然会在query的时候被pushdown导致更早的海报覆盖后来的海报

    }
}
void update(int k,int l,int r,int num){
    if(tr[k].l>=l&&tr[k].r<=r){
        tr[k].val=num;
        return;
    }if(tr[k].val)push_down(k);
    if(tr[k<<1].r>=l)update(k<<1,l,r,num);
    if(tr[k<<1|1].l<=r)update(k<<1|1,l,r,num);
}
void query(int k,int l,int r){
    if(tr[k].val&&!vis[tr[k].val]){
        vis[tr[k].val]=1;
        ans++;
        push_down(k);
        return;
    } if(l==r){
        return ;
    }
    if(tr[k].val)push_down(k);
    int mid=(l+r)>>1;
    query(k<<1,l,mid);
    query(k<<1|1,mid+1,r);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(tr,0,sizeof tr);
        memset(vis,0,sizeof vis);
        ans=0;
        int tol=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&L[i],&R[i]);
            a[++tol]=L[i];
            a[++tol]=R[i];
        }
        sort(a+1,a+1+tol);
        tol=unique(a+1,a+1+tol)-(a+1);//排序后才能用unique
        int tmp=tol;//如果直接在循环里面 会增加很多数 甚至死循环 !!循环的条件的“常数”不许更改
        for(int i=2;i<=tol;i++){
            if(a[i]-a[i-1]==2)a[++tmp]=a[i-1]+1;//这个题很重点就是给的数值不是端点而是“线段”,如果是点,夹在两个相差为2的数,原来的值就会直接被废弃,但是“线段”情况下之前的值仍然被保存
        }
        tol=tmp;
        sort(a+1,a+1+tol);
        build(1,1,tol);
        for(int i=1;i<=n;i++){
            int ll=lower_bound(a+1,a+1+tol,L[i])-(a);
            int rr=lower_bound(a+1,a+1+tol,R[i])-(a);
            update(1,ll,rr,i);
        }
        query(1,1,tol);
        printf("%d\n",ans);
    }
    return 0;
}

模板

#include<cstdio>
#define ll long long
const int N=100007;
struct node{
    int l,r;
    ll sum,lz;
}tr[N<<2];
ll a[N];
void push_up(int k){tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;}
void build(int k,int l,int r){
    tr[k].l=l,tr[k].r=r;
    if(l==r){
        tr[k].sum=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    push_up(k);
}
void push_down(int k){
    if(!tr[k].lz)return ;
    tr[k<<1].lz+=tr[k].lz;
    tr[k<<1|1].lz+=tr[k].lz;
    tr[k<<1].sum+=(tr[k<<1].r-tr[k<<1].l+1)*tr[k].lz;
    tr[k<<1|1].sum+=(tr[k<<1|1].r-tr[k<<1|1].l+1)*tr[k].lz;
    tr[k].lz=0;
}
ll query(int k,int l,int r){
    if(tr[k].l>=l&&tr[k].r<=r){
        return tr[k].sum;
    }
    push_down(k);
    ll ans=0;
    if(tr[k<<1].r>=l)ans+=query(k<<1,l,r);
    if(tr[k<<1|1].l<=r)ans+=query(k<<1|1,l,r);
    return ans;
}
void modify(int k,int l,int r,ll num){
    if(tr[k].l>=l&&tr[k].r<=r){
        tr[k].sum+=num*(tr[k].r-tr[k].l+1);
        tr[k].lz+=num;
        return;
    }
    push_down(k);//每一个“向下”的操作都需要push_down
    if(tr[k<<1].r>=l)modify(k<<1,l,r,num);
    if(tr[k<<1|1].l<=r)modify(k<<1|1,l,r,num);
    push_up(k);
    return ;
}
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    build(1,1,n);
    for(int i=1;i<=q;i++){
        char op;
        int l,r;
        scanf("\n%c%d%d",&op,&l,&r);
        if(op=='Q')printf("%lld\n",query(1,l,r));
        else if(op=='C'){
            ll num;
            scanf("%lld",&num);
            modify(1,l,r,num);
        }
    }
}

712 div2


 /*
 * a 10my 只想到应该是很直接地在某个地方插入就好 但是无法推出哪个位置
如果全部是a必然不行,否则选择插在开头或者插在结尾就好,如果不是全部都是a,其实如果a插在前面是回文,那么插在后面必然不是回文
  简单模拟一下就好
 其实真的可以大胆猜测最后答案的形式,一般规律都比较简单 几个例子都满足就先打 打了之后再去验证正确性
a_______a
 _______aa
 aa_____aa
 aa____aaa
 aaa___aaa
 aaa__aaaa
 aaaa_aaaa
 aaaaaaaaa*/
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
      string s;
      cin>>s;
      int flag=1;
      for(int i=0;i<s.length();i++)
      {
          if(s[i]!='a'){
              flag=0;
              break;
          }
      }
      if(flag)cout<<"NO"<<endl;
      else {
          string a=s+'a',b='a'+s;
          for(int i=0;i<a.length();i++)
          {
              if(a[i]!=a[a.length()-1-i])
              {
                  cout<<"YES"<<endl<<a<<endl;
                  break;
              }
              if(b[i]!=b[b.length()-1-i])
              {
                  cout<<"YES"<<endl<<b<<endl;
                  break;
              }
          }
      }
    }
}

/* b
 * 100my 前缀和的思想 把全部的操作都集中在一起完成就可以了 而且很有对称性
 * 用一个数字就可以表示当前的状态 而不必真的把它们全都进行翻转 用数字表示状态 用空间换操作时间的一种体现
*/
#include <iostream>
#include<cstring>
using namespace std;
int v,cnt[300005];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        v=0;
        memset(cnt,0,sizeof(cnt));///绝了!不可再被坑,多样例必须初始化
        int n;
        cin>>n;
        string a,b;
        cin>>a>>b;
        if(a[0]=='1')cnt[0]=1;
        for(int i=1;i<a.length();i++)
        {
            if(a[i]=='1')cnt[i]=cnt[i-1]+1;
            else cnt[i]=cnt[i-1];
        }
        if(n%2&&(a[n-1]!=b[n-1]))
        {
            cout<<"NO"<<endl;
            continue;
        }
        else
        {
            int pos=n-1,fl=1;
            if(pos==0){
                cout<<"YES"<<endl;
                continue;
            }
            else {
                if(n%2)pos--;
                while(pos!=-1){
                    if((a[pos]==b[pos]&&a[pos-1]==b[pos-1]&&v%2==0)||
                       ((a[pos]!=b[pos]&&a[pos-1]!=b[pos-1]&&v%2)));
                    else if(((a[pos]==b[pos]&&a[pos-1]==b[pos-1]&&v%2)||
                             (a[pos]!=b[pos]&&a[pos-1]!=b[pos-1]&&v%2==0))&&cnt[pos]*2==pos+1){
                        v++;
                    }
                    else {
                        cout<<"NO"<<endl;
                        fl=0;
                        break;
                    }
                    pos-=2;
                }
            }
            if(fl)cout<<"YES"<<endl;


        }
    }

    return  0;
}


/* c 30myself
 * 其实和第一题有点像,需要观察一下很明显的不可行的情况 前后是0,0个数是奇数,总个数奇数
 * 然后又把内容进行分割找出相同的地方 其实也算是题目的明显提示的 “1”表示的相同情况
 * 分类讨论,之所以对于0不可以采用和1一样的贪心策略先在前面尽量加入足够多的“(”, 是因为可能很多0在前面出现导致这种贪心会让第二排完全走向贪心的
 * 反面,也就是最差的方面,所以就找最”中庸“对两方都对等的方法。交替进行(对称性体现得太美了!)
*/

#include<iostream>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,cnt0=0;
        cin>>n;
        string s,a,b;
        cin>>s;
        if(n%2||s[n-1]=='0'||s[0]=='0'){
            cout<<"NO"<<endl;
            continue;
        }
        for(int i=0;i<s.length();i++){
            if(s[i]=='0')cnt0++;
        }
        int cnt1=n-cnt0;
        int cntt1=cnt1,cntt0=cnt0;
        if(cnt0%2){
            cout<<"NO"<<endl;
            continue;
        }
        else {
            cout<<"YES"<<endl;

            for(int i=0;i<n;i++)
            {
                if(s[i]=='1')
                {
                    if(cntt1>cnt1/2){
                        cout<<"(";
                        cntt1--;
                    }
                    else cout<<")";
                }
                else if(s[i]=='0'){
                    if(cntt0%2)cout<<"(";
                    else cout<<")";
                    cntt0--;
                }
            }cout<<endl;
            cntt1=cnt1,cntt0=cnt0;
            for(int i=0;i<n;i++)
            {
                if(s[i]=='1')
                {
                    if(cntt1>cnt1/2){
                        cout<<"(";
                        cntt1--;
                    }
                    else cout<<")";
                }
                else if(s[i]=='0'){
                    if(cntt0%2)cout<<")";
                    else cout<<"(";
                    cntt0--;
                }
            }
            cout<<endl;

        }
    }
}


/*d 30myself
 * 先给出最普适的情况 再把他的特殊要求加进去 重点就是一个数字应该放的位置放完了
 * 其他位置爱咋放咋放 思路很简单 代码用queue 存储也很简单
 */
#include <bits/stdc++.h>
using namespace std;
queue<pair<int,int>>sa,sb;
int main() {
    int n,a,b;
    cin>>n;
    //use a queue to store it!
    if(n==1)cout<<"1 1 1"<<endl;
    else {
        int tmp1;
        cin>>tmp1;
        if(tmp1==1)a=2;
        else if(tmp1==2||tmp1==3)a=1;
        printf("%d 1 1\n",a);//先确定第一,第二个位置是什么数值
        cin>>tmp1;
        if(tmp1!=a)b=6-tmp1-a;
        else {
            if(a==1)b=2;
            else if(a==2||a==3)b=1;
        }
        printf("%d 1 2\n",b);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
            {
                if((i==1&&j==1)||(i==1&&j==2))continue;
                else if(!((i+j)%2))sa.push({i,j});//让初始的数据占据整个版面 记录它们分别可以放置的位置
                else if((i+j)%2)sb.push({i,j});
            }
        }
        for(int i=3;i<=n*n;i++)
        {
            cin>>tmp1;
            if(tmp1!=a&&sa.size()){
                int x,y;
                x=sa.front().first,y=sa.front().second;
                sa.pop();
                printf("%d %d %d\n",a,x,y);
            }
            else if(tmp1!=b&&sb.size()){
                int x,y;
                x=sb.front().first,y=sb.front().second;
                sb.pop();
                printf("%d %d %d\n",b,x,y);
            }
            else {
                if(sa.size()){
                    int x,y;
                    x=sa.front().first,y=sa.front().second;
                    sa.pop();
                    printf("%d %d %d\n",6-a-b,x,y);
                }
                else if(sb.size()){
                    int x,y;
                    x=sb.front().first,y=sb.front().second;
                    sb.pop();
                    printf("%d %d %d\n",6-a-b,x,y);
                }
            }
        }


    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值