Codeforces Round #791 (Div. 2)[已补BCD,待补EF]

Codeforces Round #791 (Div. 2)

Problem - B - Codeforces

tags

单点修改,区间修改

思路

不用线段树模板的话

  1. 记录下区间修改的时间T、v记录区间修改的值,以及用t[i]记录下第i个点修改的时间
  2. 若t[i]<T则a[i]的值不是原值而是v
  3. 否则a[i]为原值

可以直接模拟,也可以用map来维护

代码

AC2
#include<iostream>
#define ll long long
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n,q;
    cin>>n>>q;
    ll a[n+1],all=0,v=0;
    int time[n+1]={0},T=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        all+=a[i];
    }
    for(int k=1;k<=q;k++){
        int op;
        cin>>op;
        if(op==1){
            int i;
            ll x;
            cin>>i>>x;
            if(time[i]<T)all+=-v+x;
            else all+=-a[i]+x;
            cout<<all<<endl;
            a[i]=x;
            time[i]=k;
        }
        if(op==2){
            ll x;
            cin>>x;
            cout<<n*x<<endl;
            all=n*x;
            T=k;
            v=x;
        }
    }
}
说明:

时间k要从1开始,不能从0开始。不然第一次为区间修改在单点的话t[i]<T会不成立

AC2

map维护

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n,q;
    cin>>n>>q;
    ll all=0,v=0;
    map<int,ll>a;
    for(int i=1;i<=n;i++){
        ll x;cin>>x;
        a[i]=x;
        all+=x;
    }
    while(q--){
        int op;cin>>op;
        if(op==2){
            ll x;cin>>x;
            cout<<n*x<<endl;
            all=n*x;
            a.clear();
            v=x;
        }
        if(op==1){
            int i,x;
            cin>>i>>x;
            if(a.count(i))all+=-a[i]+x;
            else all+=-v+x;
            cout<<all<<endl;
            a[i]=x;
        }
    }
}
说明
  1. 用c.clear()来消除区修改前的区间(相当与上面的记录T)
  2. 用c.count()判断单点修改前是否区间修改(相当于时间t[i]),若c.count(i)==0则上一次经历过区间修改

Problem - C - Codeforces

tags

线段树(单点修改,区间查询)

思路

  1. 建两颗线段树node r[],c[],r代表行,c代表列,线段树维护区间最小值
  2. 若t==1,把点x加1,点y加1
  3. 若t==2,把点x减1,点y减1
  4. 若t==3,查找区间(x1,x2)与区间(y1,y2),有一个区间最小值不为0,则yes否则no

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+10;
int n,q;
struct node{
   int l,r,x;
   node(){l=r=x=0;}
}r[4*maxn],c[4*maxn];

void update(int k,node*a){
   a[k].x=min(a[k*2].x,a[k*2+1].x);
}

void build(int k,int l,int r,node *a){
   a[k].l=l,a[k].r=r;
   if(l==r){
       a[k].x=0;
       return;
   }
   int mid=(l+r)>>1;
   build(2*k,l,mid,a);
   build(2*k+1,mid+1,r,a);
   update(k,a);
}

void change(int k,int index,node*a,int m){
   if(a[k].l==a[k].r){
       a[k].x+=m;
       return;
   }
   int mid=(a[k].l+a[k].r)>>1;
   if(index<=mid)change(2*k,index,a,m);
   if(index>mid)change(2*k+1,index,a,m);
   update(k,a);
}

int search(int k,int l,int r,node*a){
   if(a[k].l==l&&a[k].r==r){
       return a[k].x;
   }    
   int mid=(a[k].l+a[k].r)>>1;
   if(r<=mid) return search(2*k,l,r,a);
   else if(l>mid) return search(2*k+1,l,r,a);
   else return min(search(2*k,l,mid,a),search(2*k+1,mid+1,r,a));
}

void solve(){
   int t;cin>>t;
   if(t==1){
       int x,y;
       cin>>x>>y;
       change(1,x,r,1);
       change(1,y,c,1);
   }
   if(t==2){
       int x,y;
       cin>>x>>y;
       change(1,x,r,-1);
       change(1,y,c,-1);
   }
   if(t==3){
       int x1,y1,x2,y2;
       cin>>x1>>y1>>x2>>y2;
       if(search(1,x1,x2,r)||search(1,y1,y2,c))cout<<"YES"<<endl;
       else cout<<"NO"<<endl;

   }
}

int main(){
   ios::sync_with_stdio(false);
   cin.tie(0);cout.tie(0);
   cin>>n>>q;
   build(1,1,n,r);
   build(1,1,n,c);
   while(q--){
       solve();
   }
}


Problem - D - Codeforces

tags

图,二分,拓扑排序

思路

  1. 若所选的点权值越大,越容易符合k条边(有k次操作),反之,越难符合k条边
  2. 所以满足单调性,并且要最小化最大值可以用二分来搜索最大点权值,并最小化它
  3. 而对于我们最大点权值mid,需要check(mid)是否满足题目条件
  4. 把num[i]>mid的点去掉,因为我们已经确定点权最大为mid
  5. 在进行拓扑排序,找出最长边,若边长>=k则符合条件
  6. 并且,若完成while循环后还有点有入度,则有环,也是符合条件的
  7. 二分部分用左闭有闭,循环条件l<=r,用ans来记录答案,若mid满足条件,r=mid-1否则l=mid+1寻找有无更小的mid

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

typedef pair<int,int>P;
const int maxn=2e5+5,inf=0x3f3f3f3f;
int n,m;
ll k;
int num[maxn],vis[maxn],in[maxn];
vector<int>g[maxn];
vector<P>e;

bool check(int x){
    for(int i=1;i<=n;i++)vis[i]=0,in[i]=0;
    for(int i=1;i<=n;i++)if(num[i]<=x)vis[i]=1;
    for(int i=0;i<m;i++){
        if(vis[e[i].first]&&vis[e[i].second])in[e[i].second]++;
    }
    queue<int>que;
    int count=0;
    for(int i=1;i<=n;i++)if(vis[i]&&in[i]==0)que.push(i),que.push(count+1);
    while(!que.empty()){
        int st=que.front();
        que.pop();
        int c=que.front();
        que.pop();
        if(c>=k)return 1;
        for(int i=0;i<g[st].size();i++){
            int t=g[st][i];
            if(!vis[t])continue;
            in[t]--;
            if(in[t]==0)que.push(t),que.push(c+1);
        }
    }
    for(int i=1;i<=n;i++)if(vis[i]&&in[i]>0)return 1;
    return 0;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)cin>>num[i];
    for(int i=0;i<m;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        e.push_back(P(u,v));
    }
    int l=1,r=1e9;
    int ans=inf;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid)){
            ans=min(ans,mid);
            r=mid-1;
        }
        else l=mid+1;
    }
    if(ans==inf)cout<<-1<<endl;
    else cout<<ans<<endl;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值