The 2023 ICPC Asia Shenyang Regional Contest (The 2nd Universal Cup. Stage 13: Shenyang)

题目链接

C题 Swiss Stage

思路:

签到题,就是只要赢两局或者输两局之后的每一局都要进行三局两胜的对局,也就是说在没有赢两局或者输两局的时候,赢数加1只需要赢一局即可,但有的话,赢一局需要赢两局。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=1e5+10;
const int mod=1e9+7;
#define fi first
#define se second

void solve(){
	int a,b;cin>>a>>b;
	int ans=0;
	while(a<2){
		if(b==2) ans+=2;
		else ans+=1;
		a++;
	}
	ans+=2;
	cout<<ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

J题 Graft and Transplant

思路:

容易发现如果操作的点之间没有叶子节点,那么产生的树是新树,否则产生的树是旧树,所以我们只要判断度数大于1的点的个数即可,但要特判0的情况。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=1e5+10;
const int mod=1e9+7;
#define fi first
#define se second

int n;
int du[N];
void solve(){
	cin>>n;
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		du[a]++,du[b]++;
	}
	int sum=0;
	for(int i=1;i<=n;i++){
		if(du[i]>1) sum++;
	}
	if(sum==0){
		cout<<"Bob";
		return;
	}
	if((sum&1)==0) cout<<"Alice";
	else cout<<"Bob";
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

E题 Sheep Eat Wolves

思路:

用个三维数组记录一下左边羊,狼以及人的位置来一次广搜即可。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e6+10;
const int M=1e6+10;
const int inf=0x3f3f3f3f;
int x,y,p,q; 
int ans=inf;


int vis[200][200][3];
struct node{
    int ls, lw, rs, rw, pos, res;
};
void bfs(){
    queue<node> qq;
    qq.push({x, y, 0, 0, 0, 0});
    while(!qq.empty()){
        node t = qq.front();
        qq.pop();
        if(vis[t.ls][t.lw][t.pos]) continue;
        if(t.rs==x){
            ans=min(ans,t.res);
            return;
        }
        if(t.lw>t.ls+q&&t.pos==1 && t.ls != 0){
            continue;
        }
        if(t.rw>t.rs+q&& t.pos==0 && t.rs != 0){
            continue;
        }
        vis[t.ls][t.lw][t.pos] = 1;
        if(t.pos == 0){
            for(int i=0;i<=t.ls && i <= p ;i++){
			    for(int j=0;j<=t.lw && i + j <= p;j++){
				   qq.push({t.ls - i, t.lw -j, t.rs + i, t.rw + j, 1, t.res + 1});
			    }
		    }
        }else{
            for(int j=0;j<=t.rw && j <= p;j++){
				qq.push({t.ls, t.lw+j, t.rs, t.rw-j, 0, t.res+1});
			}
        }
    }
}


void solve(){
	cin >> x >> y >> p >> q;
	bfs();
	if(ans!=inf)
	    cout <<ans;
	else
	    cout << -1;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int _=1;
	//cin >> _;
	while(_--){
		solve();
	}
}


K题  Maximum Rating

思路:

        对于最小的操作数即是将所有负数放在前面,再将所有正数从小到大放在后面,操作数即是前缀和大于等于1的个数。

        对于最大的操作数即是将所有正数放在前面,然后操作数就是正数个数。

        在最大操作数的前提下,我们每次将一个最前端的正数移到最后端,很容易知道操作数要么减一要么不变,又最终可以变成最小的操作数,所以对于最小操作数到最大操作数之间是连续的。

        所以我们只需要求出每次的最小操作数l和最大的操作数r,答案就是r-l+1。

        对于最大操作数我们只需要每次维护出正数个数即可,用sum1来记录,若修改的是正数则sum1--,若改成的值是正数则sum1++。

        对于最小操作数,则是求每次操作完后的新数组按上述排序后前缀和大于等于1的个数。由于我们需要排序又需要求个数,我们考虑用权值线段树,因为权值线段树的叶子节点是从小到大排序且每次可以log的修改每个值的个数。

        因为数据范围很大,但询问和数组大小只有1e5,我们考虑离线处理,先将所有数据读入再进行离散化。

        我们要找前缀和大于等于1的位置,然后用总数组长度减去当前位置,即是前缀和大于等于1的个数。

        先介绍nlog^2的做法:

        由于前缀和具有单调性,我们可以二分区间【1,r】,找到第一个前缀和大于等于1的在线段树中的位置。当我们找到当前位置的时候我们再去查询当前位置的前缀个数和。由于叶子节点的cnt值不一定是1,所以我们还需要找到最左边的位置,为了找到最左边的位置我们先要把当前位置的前缀值s和真实值x找出来,然后二分条件s-mid*x>0就再往左边找。

        代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=1e6+10;
const int mod=1e9+7;
#define fi first
#define se second
#define int ll
const ll INF = -0x3f3f3f3f3f3f3f3f;
int n,q;
int a[N];
vector<int> v;
vector<pii> ask;
int len,sum1,sum2,sum;
int Find(int x){
    int pos= lower_bound(v.begin(),v.end(),x)-v.begin();
    return pos;
}
int tree[N*4],cnt[N*4];
void insert(int p,int l,int r,int x,int op){
    if(l==r&&l==x){
        cnt[p]+=op;
        tree[p]+=v[x]*op;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) insert(p<<1,l,mid,x,op);
    else insert(p<<1|1,mid+1,r,x,op);
    tree[p]=tree[p<<1]+tree[p<<1|1];
    cnt[p]=cnt[p<<1]+cnt[p<<1|1];
}
int query(int p,int l,int r,int x,int y){
    int res=0;
    if(x<=l&&r<=y){
        return tree[p];
    }
    int mid=(l+r)>>1;
    if(x<=mid) res+=query(p<<1,l,mid,x,y);
    if(y>mid) res+=query(p<<1|1,mid+1,r,x,y);
    return res;
}
int querycnt(int p,int l,int r,int x,int y){
    int res=0;
    if(x<=l&&r<=y){
        return cnt[p];
    }
    int mid=(l+r)>>1;
    if(x<=mid) res+=querycnt(p<<1,l,mid,x,y);
    if(y>mid) res+=querycnt(p<<1|1,mid+1,r,x,y);
    return res;
} 
int queryvalue(int p,int l,int r,int x){
    int res=0;
    if(l==r){
        return tree[p]/cnt[p];
    }
    int mid=(l+r)>>1;
    if(x<=mid) res+=queryvalue(p<<1,l,mid,x);
    if(x>mid) res+=queryvalue(p<<1|1,mid+1,r,x);
    return res;
} 
void solve(){
    cin>>n>>q;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]>0) sum1++;
        else sum2++;
        v.push_back(a[i]);
        sum+=a[i];
    }
    for(int i=1;i<=q;i++){
        int a,b;cin>>a>>b;
        ask.push_back({a,b});
        v.push_back(a);
        v.push_back(b);
    }
    v.push_back(INF);
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    len=v.size()-1;
    for(int i=1;i<=n;i++){
        insert(1,1,len,Find(a[i]),1);
    }
    for(int i=1;i<=q;i++){
        int aa=ask[i-1].first,b=ask[i-1].second;
        insert(1,1,len,Find(a[aa]),-1);
        insert(1,1,len,Find(b),1);
        if(a[aa]>0){
            sum1--;
        }
        else{
            sum2--;
        }
        if(b>0){
            sum1++;
        }
        else{
            sum2++;
        }
        sum-=a[aa];sum+=b;
        a[aa]=b;
        if(sum<=0){
            cout<<sum1+1<<endl;
            continue;
        }
        int l=1,r=len;
        while(l<=r){
            int mid=(l+r)>>1;
            if(query(1,1,len,1,mid)>=1) r=mid-1;
            else l=mid+1;
        }
        int s=query(1,1,len,1,l);
        int pos=querycnt(1,1,len,1,l);
        int x=queryvalue(1,1,len,l);
        int nl=1,nr=n;
        while(nl<=nr){
            int mid=(nl+nr)>>1;
            if(s-mid*x>=1) nl=mid+1;
            else nr=mid-1;
        }
        pos-=nr;
        pos=n-pos+1;
        cout<<sum1-pos+1<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

       接下来我们讲nlog的做法:

        我们考虑优化掉二分查找位置时的二分算法。

        由于我们要找的是前缀和大于等于1的位置,所以我们查询到一个位置它的左半边的和小于等于需要的和,我们直接加上左半边的和,然后去右边找剩下的和。

        找到位置后剩下的操作和上面一个方法一样,先找前缀和,然后找真实值,二分找到最左端位置。对于寻找真实值的时候,我们仍然要求出该值在线段树中的位置,我们只需要在查询的时候加个pp来记录查询到的叶子节点即可,因为如果要查找的和大于左边的时候,左半块我们是不会进去的,也就是说我们每次都是往右边查询,也就是说我们只会找到最右端的位置,也就是我们需要的第一个大于等于1前缀和的位置。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=1e6+10;
const int mod=1e9+7;
#define fi first
#define se second
#define int ll
const int INF = -0x3f3f3f3f3f3f3f3f;
int n,q;
int a[N];
vector<int> v;
vector<pii> ask;
int len,sum1,sum2,sum;
int pp;
int Find(int x){
	int pos= lower_bound(v.begin(),v.end(),x)-v.begin();
	return pos;
}
int tree[N*4],cnt[N*4];
void insert(int p,int l,int r,int x,int op){
	if(l==r&&l==x){
		cnt[p]+=op;
		tree[p]+=v[x]*op;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) insert(p<<1,l,mid,x,op);
	else insert(p<<1|1,mid+1,r,x,op);
	tree[p]=tree[p<<1]+tree[p<<1|1];
	cnt[p]=cnt[p<<1]+cnt[p<<1|1];
}
int query(int p,int l,int r,int x){
	int res=0;
	if(l==r){
		//if(cnt[p])
		pp=max(pp,l);
		return cnt[p];
	}
	int mid=(l+r)>>1;
	if(tree[p<<1] < x){
		res+=cnt[p<<1];
		res+=query(p<<1|1,mid+1,r,x-tree[p<<1]);
	} 
	else{
		res+=query(p<<1,l,mid,x);
	}
	return res;
}
int querysum(int p,int l,int r,int x){
	int res=0;
	if(tree[p]<=x||l==r){
		return tree[p];
	}
	int mid=(l+r)>>1;
	if(tree[p<<1] < x){
		res+=tree[p<<1];
		res+=querysum(p<<1|1,mid+1,r,x-tree[p<<1]);
	} 
	else{
		res+=querysum(p<<1,l,mid,x);
	}
	return res;
}
int queryvalue(int p,int l,int r,int x){
	int res=0;
	if(l==r){
		return tree[p]/cnt[p];
	}
	int mid=(l+r)>>1;
	if(x<=mid) res+=queryvalue(p<<1,l,mid,x);
	if(x>mid) res+=queryvalue(p<<1|1,mid+1,r,x);
	return res;
} 
void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]>0) sum1++;
		else sum2++;
		v.push_back(a[i]);
		sum+=a[i];
	}
	for(int i=1;i<=q;i++){
		int a,b;cin>>a>>b;
		ask.push_back({a,b});
		v.push_back(a);
		v.push_back(b);
	}
	v.push_back(INF);
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	len=v.size()-1;
	for(int i=1;i<=n;i++){
		insert(1,1,len,Find(a[i]),1);
	}
	for(int i=1;i<=q;i++){
		int aa=ask[i-1].first,b=ask[i-1].second;
		insert(1,1,len,Find(a[aa]),-1);
		insert(1,1,len,Find(b),1);
		if(a[aa]>0){
			sum1--;
		}
		else{
			sum2--;
		}
		if(b>0){
			sum1++;
		}
		else{
			sum2++;
		}
		sum-=a[aa];sum+=b;
		a[aa]=b;
		if(sum<=0){
			cout<<sum1+1<<endl;
			continue;
		}
		pp=0;
		int x=1;
		int l=1,r=len;
		l=query(1,1,len,1);
		int s=querysum(1,1,len,1);
		int pos=l;
		int y=queryvalue(1,1,len,pp);
		int nl=1,nr=n;
		while(nl<=nr){
			int mid=(nl+nr)>>1;
			if(s-mid*y>=1) nl=mid+1;
			else nr=mid-1;
		}
		pos-=nr;
		pos=n-pos+1;
		cout<<sum1-pos+1<<endl;
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值