Codeforces Round #149 (Div. 2)

本文详细解析了Codeforces Round #149 (Div. 2)的比赛题目,包括A. Heads or Tails、B. Big Segment、C. King's Path、D. Dispute和E. XOR on Segment。涉及最短路径问题、图论策略、区间异或操作等算法,适合编程爱好者学习和练习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


A. Heads or Tails

第一题 比较简单,我直接发代码了。

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(){
    int i, j, x, y, a, b, ans = 0;
    scanf("%d%d%d%d",&x,&y,&a,&b);
    
    for(i = a; i <= x; i++)
	for(j = b; j < i && j <= y; j++)
	    ans++;

    printf("%d\n",ans);
    for(i = a; i <= x; i++)
	for(j = b; j < i && j <= y; j++)
	    printf("%d %d\n",i, j);

    return 0;
}

B. Big Segment

第二题同样是简单题,同样只发代码

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

const int maxn = 200000;
struct Segment{
    int l, r;
};

Segment seg[maxn];

int main(){
    int i, j, l, r, n;
    scanf("%d",&n); 
    for(i = 0; i < n; i++)
	scanf("%d%d",&seg[i].l,&seg[i].r);

    l = seg[0].l;
    r = seg[0].r;

    for(i = 1; i < n; i++){
	if(l > seg[i].l) l = seg[i].l;
	if(r < seg[i].r) r = seg[i].r;
    }

    int num = -1; 
    for(i = 0; i < n; i++)
	if(l == seg[i].l && r == seg[i].r){
	    num = i+1;
	    break;
	}

    printf("%d\n",num);
    return 0;
}


C. King's Path

题意:给定10^9*10^9的国际象棋棋盘,吿诉你一些地方不能走,求国王从起点走到给写的终点最少要走几步。

输入:第一行:x0,y0,x1,y1(1 <= x0,y0,x1,y1<=10^9)表示起点和终点

          第二行:n 表示有n个可走的区域(1<=n<=10^5)

          第三行到第2+n行:ri,ai,bi把是r行从ai到bi列可走

输出:最少的步数

题解:这是最短路的问题,若直接用相互的最短路算法过不了。可以直接用以spfa+优先队列优化,也可以认为是广搜+优先队列。

代码:

#include <map>
#include <queue>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX(x,y) ((x)>(y)?(x):(y))
#define ABS(x) ((x) >= 0? (x):(-x))

using namespace std;
const int INF = 1200000000;

const int dx[] = {-1,-1,-1,+0,+1,+1,+1,+0};
const int dy[] = {-1,+0,+1,+1,+1,+0,-1,-1};

int n, x0, y0, x1, y1;

struct Node{
    int x, y, step;
    bool operator<(const Node& n)const{
	int dis1 = step + MAX(ABS(x-x1),ABS(y-y1));		    
	int dis2 = n.step + MAX(ABS(n.x-x1),ABS(n.y-y1));
	return !(dis1 < dis2 || dis1 == dis2 && step < n.step);
    }
};

priority_queue<Node> que;
map<pair<int,int>,int> mpi;

int main(){
    int i, j, a, b, r;
    scanf("%d%d%d%d%d",&x0,&y0,&x1,&y1,&n);

    for(i = 0; i < n; i++){ 
	scanf("%d%d%d",&r,&a,&b);
	for(j = a; j <= b; j++)
	    mpi[make_pair(r,j)] = INF;
    }
    
    mpi[make_pair(x0,y0)] = 0;
    mpi[make_pair(x1,y1)] = INF;
    
    Node begin;
    begin.x = x0;
    begin.y = y0;
    begin.step = 0;
    que.push(begin);

    while(!que.empty()){
	int x = que.top().x;
	int y = que.top().y;
	int step = mpi[make_pair(x,y)];
	que.pop();

	for(i = 0; i < 8; i++){
	    int u = x + dx[i];
	    int v = y + dy[i];
	    if(u > 0 && u < 1100000000)
	    if(v > 0 && v < 1100000000)
	    if(mpi.find(make_pair(u,v)) != mpi.end()){
		if(mpi[make_pair(u,v)] > step+1){
		    Node now;
		    now.step = step + 1;
		    now.x = u, now.y = v;
		    mpi[make_pair(u,v)] = step + 1;
		    que.push(now);
		}
	   }
	}
    }

    int ans = mpi[make_pair(x1,y1)];
    printf("%d\n",ans == INF? -1 : ans);

    return 0;
}

D. Dispute

题意:给定一个无向图,图中的每个结点最初的权值都为0。但每个结点有一次机会,可以增加它本身的权值,但是有个副作用即:在增加自己权值的情况下,也会增加与自己直接相连的结点的权值。 Ignat和Valera打赌说:valera不可能找到一种方法,使得对于每个结点i,它的权值不等于lgnat给定的ai。然后,题目要求写个程序,让valera可以胜,并输出哪个结点要用到,它给定的那次增加的机会。

输入:第1行n,m(1<=n,m<=10^5)分别表示有n个结点,和m条边

          第2行到第m+1行每行有两个数u,v,表示结点u,v间有一条边

           第m+2行,有n个数表示Ignat 给的ai

输出:若Ignal可以胜,就输出那个有用掉那个机会的结点,否则输出-1

题解:我们可以证明Valera必胜。它的必胜策略如下:

          第一步:找到a[i]中所有为0的结点,若没有则我们可以一个机会都不用,那样的话Valera必胜;若有结点的a[i] == 0则把i,加入队中,转入第二步

          第二步:当队为空时结束,否则取出队首。让队首用掉那次可以增加本身,及和它相连的机会。并把与队首相连的元素(比如j) 的权值减1。若它的值为0,则,入队。一直做第二步直到队空

         我们用数学归纳法证明上述算法的正确性:

         归纳基础:易知当只有一个元素时,valera必胜,并可以知道上述算法可行

         归纳过程:我们假设当有少于n(n > 1)个结点的图时,按上述算法可得到一个最优解。我们假设在最优解的n个结点中,有x个结点用掉了那个机会,有y个结点没有用掉那个机会。若新增加的结点和有用掉那个机会的x中的a个有连边,和没用掉那个机会的b个有连边,如果a[n+1] != a 且 a[n+1] != 0,则,按上述算法,增加这个点对于答案没有任何影响。如果a[n+1] = a 或者 a[n+1] = 0,我们就把把用掉那个机会的x个元素从图中去掉,并把相应的a做改变则可以转为少于n的图,由归纳假设可以知,少于n+1的图一定有解。这边还有一个情况没说,如果x = 0, 那么不能转化为少于n的情况,假设出现这种情况,那么说明,只有a[n+1] = 0,其它的都 大于0,我们可以把,图中与n+1,相连的结点的权值都 减一,并把n+1,这个结点加入答案中,那么同样可以转化为少于n+1个结点的图。证毕。

代码:

#include <queue>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;

const int maxn = 120000;

int a[maxn];
queue<int> que;
vector<int> ans;
vector<int> edge[maxn];

int main(){
    int i, j, n, m;
    scanf("%d%d",&n,&m);
    for(i = 0; i < m; i++){
	int u, v;
	scanf("%d%d",&u,&v);
	edge[u].push_back(v);
	edge[v].push_back(u);
    }

    for(i = 1; i <= n; i++){
	scanf("%d",a+i);
	if(a[i] == 0)
	    que.push(i);
    }

    while(!que.empty()){
	int now = que.front();
	que.pop();
	ans.push_back(now);
	for(i = 0; i < (int)edge[now].size();i++){
	    int next = edge[now][i];
	    --a[next];
	    if(a[next] == 0)
		que.push(next);
	}
    }

    printf("%d\n",ans.size());
    for(i = 0; i < (int)ans.size();i++)
	if(i == (int)ans.size()-1)
	    printf("%d\n",ans[i]);
	else
	    printf("%d ",ans[i]);
    return 0;
}

E. XOR on Segment

题意:给定一个数组对数组有两个操作:

            1、寻问[l,r]这个区间数的总和

             2、把[l,r]中的每个数都 与 x 做异或

输入:第1行:给定一个n(1 <= n <= 10^5)

             第2行:给定整个数组

             第3行:给一个m(1 <= m <= 5*10^5)表示操作的总数

              第4到第3+m行:每行只有两种类型的输入

                     第一种:  1,l,r   表示寻问[l,r]的数的总和

                     第二种:2,l,r ,x 表示把[l,r]的每个数都异或x 

输出:对于每个1操作,输出每次的总和

题解:要求对给定的区间进行操作,自然想到线段树,不过由于异或不然满足(a+b)^x = a^x+b^x,固,不能只建一棵树,还是要建32棵(实际上只要20棵就行了),即把每一个int的数,看成是由32位的二进制进行表示,这样我们只要知道给定区间内有多少个1,我们就可以算出这个数是多少了。在异或运算中,如果是0,则什么都 不用做,如果是1,那么这个区间内所有的0,1互相调换。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#define LSon(x) ((x) << 1)
#define RSon(x) ((x) << 1 | 1)
using namespace std;

const int maxn = 230000;

struct Seg{
    int w;
    int flag;
};


struct SegTree{
    Seg node[maxn << 2];
    void update(int pos){
	//write some code
	node[pos].w = node[LSon(pos)].w + node[RSon(pos)].w;		
    }

    void init(int l, int r, int pos){
	//write some code
	node[pos].flag = 0;
	if(l == r){
	    node[pos].w = 0;
	}else{
	    int m = l + r >> 1;
	    init(l,m,LSon(pos));
	    init(m+1,r,RSon(pos));
	    update(pos);
	}
    }

    void push(int l, int r, int pos){
	Seg& father = node[pos];
	Seg& lson = node[LSon(pos)];
	Seg& rson = node[RSon(pos)];
	int len = r - l + 1;
	if(father.flag){
	    father.flag = 0;
	    //write some code
	    lson.w = ((len+1) >> 1)-lson.w;
	    lson.flag ^= 1;
	    //write some code
	    rson.w = (len >> 1) - rson.w;
	    rson.flag ^= 1;
	}
    }

    void modify(int l, int r, int x, int y, int z, int pos){
	if(x <= l && r <= y){
	    //write some code
	    node[pos].w = (r - l + 1) - node[pos].w;	    
	    node[pos].flag ^= 1;
	}else{
	    push(l, r, pos);
	    int m = l + r >> 1;
	    if (x <= m) modify(l,m,x,y,z,LSon(pos));
	    if (y > m)  modify(m+1,r,x,y,z,RSon(pos));
	    update(pos);
	}
    }

    int query(int l, int r, int x, int y, int pos){
	if(x <= l && r <= y){
	    return node[pos].w;
	}else{
	    push(l,r,pos);
	    int m = l + r >> 1;
	    int ans = 0;
	    if(m >= x) ans += query(l,m,x,y,LSon(pos));
	    if(m < y)  ans += query(m+1,r,x,y,RSon(pos));
	    return ans;
	}
    }
};

SegTree tree[32];

int main(){
    int i, j, n, m, now;
    scanf("%d",&n);

    for(i = 0; i < 32; i++)
	tree[i].init(1,n,1);

    for(i = 1; i <= n; i++){
	scanf("%d",&now);
	int idx = 0;
	while(now != 0){
	    if (now & 1) 
		tree[idx].modify(1,n,i,i,0,1); 
	    ++idx;
	    now >>= 1;
	}
    }

    scanf("%d",&m);
    int x, y, t, z;

    for(i = 0; i < m; i++){
	scanf("%d%d%d",&t,&x,&y);
	if(t == 1){	
	    long long ans = 0;
	    long long base = 1;
	    for(int j = 0; j < 32; j++){  
              long long now = tree[j].query(1,n, x, y,1);
	      ans += (now * base);
	      base = base * 2;
	    }
	    cout << ans << endl;
	}else{
	    int idx = 0; 
	    scanf("%d",&z);
	    while(z != 0){
		if(z & 1)
		    tree[idx].modify(1,n,x,y,0,1);
		++idx;
		z >>= 1;
	    }
	}
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值