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;
}