您的位置:首页 > 其它

AtCoder Regular Contest 063题解

2016-11-08 16:29 417 查看
链接:AtCoder Regular Contest 063

C. 一次元リバーシ

  一道大水题,统计有多少颜色不同的段减1就好了。复杂度O(n)。

#include<bits/stdc++.h>
const int N = 100010;

char s
;

int main()
{
scanf("%s", s);
int cnt = 0, len = strlen(s);
for (int i = 1; i < len; i++)
if (s[i] != s[i - 1])
cnt ++;
printf("%d\n", cnt);
return 0;
}


D. 高橋君と見えざる手

  第二道大水题。容易知道,高桥会选择差价最大的两个城市来买卖苹果(当然,只能在前面的城市买进,在后面的城市卖出)。于是青木就只需要把每对差价等于最大差价的城市修改1就好了。本来这道题还需要讨论很多,但是都被输入限制住了。。复杂度O(n)

#include<bits/stdc++.h>
const int N = 100010;

int n, t, a
, max
;

int main()
{
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
for (int i = n; i; i --)
{
max[i] = max[i + 1];
if (a[i] > max[i])
max[i] = a[i];
}
for (int i = 1; i <= n; i ++)
a[i] = max[i] - a[i];
int ans = 0;
for (int i = 1; i <= n; i ++)
if (ans < a[i])
ans = a[i];
int cnt = 0;
for (int i = 1; i <= n; i ++)
if (a[i] == ans)
cnt ++;
return printf("%d\n", cnt), 0;
}


E. 木と整数

  任取一个已经填了数的结点作为根结点。首先,我们可以确定每一个结点的奇偶性,如有矛盾退出程序。之后我们从叶子结点开始回溯,对于叶子结点,若没有填数字,区间置为[−∞,∞],若填了数a,则置为[a,a]。对于非叶子结点,区间置为⋂i是该结点的孩子[xi,yi],若这个结点已经填了数a,则要先判断是否产生了矛盾,若无矛盾则置为[a,a]。如果回溯到根结点仍然没有矛盾,说明存在可行解。从根结点开始填数,只要注意与父亲结点差1,并且在范围内,一定能满足题目要求。复杂度O(n)。

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;

struct point
{
int num, first, left, right;
bool visited, odd;
};

struct edge
{
int next, to;
};

int cnt = 1, n, k;
edge e[N << 1];
point p
;

void addedge(int u, int v)
{
e[++ cnt].next = p[u].first;
p[u].first = cnt;
e[cnt].to = v;
e[++ cnt].next = p[v].first;
p[v].first = cnt;
e[cnt].to = u;
}

void dfs1(int i)
{
p[i].visited = true;
if (~p[i].num && p[i].odd != (p[i].num & 1))
{
printf("No\n");
exit(0);
}
for (int j = p[i].first; j; j = e[j].next)
{
int x = e[j].to;
if (p[x].visited)
continue;
p[x].odd = !p[i].odd;
dfs1(x);
p[i].left = max(p[i].left, p[x].left - 1);
p[i].right = min(p[i].right, p[x].right + 1);
}
if (p[i].left > p[i].right)
{
printf("No\n");
exit(0);
}
}

void dfs2(int i)
{
p[i].visited = true;
for (int j = p[i].first; j; j = e[j].next)
{
int x = e[j].to;
if (p[x].visited)
continue;
if (p[i].num - 1 >= p[x].left && p[i].num - 1 <= p[x].right)
p[x].num = p[i].num - 1;
else
p[x].num = p[i].num + 1;
dfs2(x);
}
}

int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
{
p[i].num = -1;
p[i].left = -1e9;
p[i].right = 1e9;
}
for (int i = 0, u, v; i < n - 1; i ++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
scanf("%d", &k);
int root;
for (int i = 0, x, num; i < k; i ++)
{
scanf("%d%d", &x, &num);
p[x].num = num;
p[x].left = num;
p[x].right = num;
root = x;
}
p[root].odd = p[root].num & 1;
dfs1(root);
for (int i = 1; i <= n; i ++)
p[i].visited = false;
dfs2(root);
printf("Yes\n");
for (int i = 1; i <= n; i ++)
printf("%d\n", p[i].num);
return 0;
}


F. すぬけ君の塗り絵 2

  首先,容易证明答案大于2max(W,H)+2:取x坐标相邻的两点,左边的点全部向左涂黑,右边的点全部向右涂黑,得到的周长最小为2H+2,同理对y坐标,周长最小为2W+2。这样我们就有了一个结论:所得的矩形至少与x=W2和y=H2两条直线之一相交(用反证法易证)。下面,不妨设矩形与x=W2相交。也就是说,在x=W2左边的点,只能向左、上、下三个方向涂色,右边的点只能向右、上、下三个方向涂色。那么,如果我们再固定最终答案的上边界和下边界,那么每个点的涂色方向都确定了:上边界以上的点向上涂色,下边界以下的点向下涂色,上下边界之间的点,在x=W2左边的向左涂色,右边的向右涂色。但是,如果直接两两枚举,显然会超时,这时候我们就需要一些优化。我们用一个线段树来维护,按y坐标从小到大枚举每个点,对于每个y坐标小于它的结点,我们记录,以当前枚举到的结点为上边界,以y小的结点为下边界,按照上面所说的涂法,得到的白色矩形的长(沿x轴方向)减去y小的结点的y坐标。这样,这个矩形的周长就是记录的这个值,加上当前枚举的结点的y坐标,再乘以2。显然,对于我们枚举的每个结点,其为上边界时的最大周长,就是所有记录的值中的最大值加上其y坐标乘以2。那么我们就在线段树中维护这些记录的最大值。接下来我们考虑一下如何更新线段树:每次枚举完一个点后,我们就要更新之前所有的点,这样显然会超时。那么我们看一下哪些点需要被更新。不妨只考虑x≤W2的点,如果该点的坐标x1小于刚被枚举的点x2,那么这个点为下界时长的值就要减去x2−x1,同时我们注意到,更新完之后,该点的长的值已经和刚枚举完的结点一样了,也就是说,我们以后碰到一个横坐标更大的点再来更新时,这两个点减去的值是一样的。所以,我们可以用一个栈来记录未被更新过的结点,已被更新过的结点则一定和最近的y坐标更大且未被更新过的结点值相同,这样我们就可以修改区间来修改线段树的值了。显然,每个点只进出栈一次。x>W2时的情况也类似。然后对于与y=H2相交的情况再同样地讨论即可(具体实现时可以直接把整个图关于y=x对称一下)。复杂度O(nlogn)。

#include<bits/stdc++.h>
const int INF = 1e9;
const int N = 300010;
const int MAX = 19;
using namespace std;

struct segment{
int lazy, rmax;
};

int w, h, n, x
, y
;
segment seg[1 << MAX + 1];
vector <pair <int, int>> p;
vector <int>_y;
stack <pair <int, int>> ls, rs;

int ysit(int y){
return lower_bound(_y.begin(), _y.end(), y) - _y.begin();
}

void build(){
for (int i = 1 << MAX; i < (1 << MAX) + _y.size(); i ++)
seg[i].rmax = w - _y[i - (1 << MAX)];
for (int i = (1 << MAX) + _y.size(); i < 1 << MAX + 1; i ++)
seg[i].rmax = -INF;
for (int i = (1 << MAX) - 1; i; i --)
seg[i].rmax = max(seg[i << 1].rmax, seg[(i << 1) + 1].rmax);
}

void add(int left, int right, int point, int depth, int v){
int l = point << depth, r = point + 1 << depth;
if (depth){
seg[point << 1].rmax += seg[point].lazy;
seg[point << 1].lazy += seg[point].lazy;
seg[(point << 1) + 1].rmax += seg[point].lazy;
seg[(point << 1) + 1].lazy += seg[point].lazy;
}
seg[point].lazy = 0;
if (l == left && r == right + 1){
seg[point].rmax += v;
seg[point].lazy += v;
return;
}
int mid = l + r >> 1;
if (right < mid)
add(left, right, point << 1, depth - 1, v);
else if (left >= mid)
add(left, right, (point << 1) + 1, depth - 1, v);
else{
add(left, mid - 1, point << 1, depth - 1, v);
add(mid, right, (point << 1) + 1, depth - 1, v);
}
seg[point].rmax = max(seg[point << 1].rmax, seg[(point << 1) + 1].rmax);
}

void add(int left, int right, int v){
if (left > right)
return;
add(left + (1 << MAX), right + (1 << MAX), 1, MAX, v);
}

int query(int left, int right, int point, int depth){
int l = point << depth, r = point + 1 << depth;
if (depth){
seg[point << 1].rmax += seg[point].lazy;
seg[point << 1].lazy += seg[point].lazy;
seg[(point << 1) + 1].rmax += seg[point].lazy;
seg[(point << 1) + 1].lazy += seg[point].lazy;
}
seg[point].lazy = 0;
if (l == left && r == right + 1)
return seg[point].rmax;
int mid = l + r >> 1, ret = -INF;
if (right < mid)
return query(left, right, point << 1, depth - 1);
if (left >= mid)
return query(left, right, (point << 1) + 1, depth - 1);
ret = max(ret, query(left, mid - 1, point << 1, depth - 1));
ret = max(ret, query(mid, right, (point << 1) + 1, depth - 1));
return ret;
}

int query(int left, int right){
if (left > right)
return -INF;
return query(left + (1 << MAX), right + (1 << MAX), 1, MAX);
}

int solve(){
memset(seg, 0, sizeof(seg));
p.clear();
_y.clear();
while (!ls.empty())
ls.pop();
while (!rs.empty())
rs.pop();
ls.push({0, w / 2});
rs.push({0, w / 2});
for (int i = 0; i < n; i ++)
p.push_back({y[i], x[i]});
sort(p.begin(), p.end());
p.push_back({h, 0});
for (int i = 0; i < n; i ++)
_y.push_back(y[i]);
_y.push_back(0);
_y.push_back(h);
sort(_y.begin(), _y.end());
_y.erase(unique(_y.begin(), _y.end()), _y.end());
build();
int ret = 0;
for (int i = 0; i < p.size(); i ++){
int X = p[i].second, Y = p[i].first;
ret = max(ret, query(0, ysit(Y) - 1) + Y);
if (X <= w / 2){
int lastx = 0, lasty = Y;
while (!ls.empty() && ls.top().second < X){
add(ysit(ls.top().first), ysit(lasty) - 1, lastx - X);
lastx = ls.top().second;
lasty = ls.top().first;
ls.pop();
}
add(ysit(ls.top().first), ysit(lasty) - 1, lastx - X);
ls.push({Y, X});
}
else{
int lastx = w, lasty = Y;
while (!rs.empty() && rs.top().second > X){
add(ysit(rs.top().first), ysit(lasty) - 1, X - lastx);
lastx = rs.top().second;
lasty = rs.top().first;
rs.pop();
}
add(ysit(rs.top().first), ysit(lasty) - 1, X - lastx);
rs.push({Y, X});
}
}
return ret;
}

int main()
{
scanf("%d%d%d", &w, &h, &n);
w *= 2;
h *= 2;
for (int i = 0; i < n; i ++){
scanf("%d%d", &x[i], &y[i]);
x[i] *= 2;
y[i] *= 2;
}
int ans = solve();
swap(w, h);
for (int i = 0; i < n; i ++)
swap(x[i], y[i]);
ans = max(ans, solve());
return printf("%d\n", ans), 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: