您的位置:首页 > 其它

「备战PKUWC2018」The 2017 ACM-ICPC Asia Shenyang Regional Contest 「HDU6217-6229」

2018-01-18 21:01 706 查看
感觉这场比赛质量不是很高,不是打表找规律就是毒瘤坑点,还有高精度(为什么不能交 Python),(还是我比较弱,做不了后面的神题)

比赛结果:



A - BBP Formula

坑。

B - Bridge

题意:有一个 2×N 的网格图,相邻的格子之间连边,显然最开始共有 3×N−2 条边。有 M 次操作,每次操作都是形如 opt x0 y0 x1 y1 的形式,若 opt=1 ,则添加 ( x0, y0)↔( x1, y1) 的边,若 opt=2 ,则删除 ( x0, y0)↔( x1, y1) 的边。保证每次操作的边都在最开始的图中存在,即 ( x0, y0) 与 ( x1, y1) 在原图中相邻。(类似 「SHOI2008」堵塞的交通)。你需要在每次操作之后,输出这张图里割边(桥)的条数。

N,M≤2×105 。

题解:

毒瘤数据结构题。对于这张网格图建一棵线段树。每个线段树对应原图的一个矩形(特别的,若 l=r ,则对应两个上下格子所连的一条竖边)。每个 l<r 的线段树节点维护连接左右儿子的横向边。

每个节点维护该区间左上,左下,右上,右下四个角向中间的横向边最长长度,最左边和最右边的纵向边是哪一条,是否在一个环上,与这个区间的答案。

合并时考虑横跨两个区间的横向边是否讲左右区间连成环,与把几条割边加入环,以此更新答案。

实现细节很多,代码量毒瘤,pushup函数共 58 行(我大括号不换行)。

My Code:

#include <bits/stdc++.h>

#define lc k << 1
#define rc k << 1 | 1

using namespace std;
typedef long long ll;

int n, m;

struct node{
int l, r;
int dat[2], dat1[2], dat2[4], dat3[2], dat4;
};

struct Seg{
node d[800005];

void pushup(int k){
int l1 = d[lc].r - d[lc].l;
int l2 = d[rc].r - d[rc].l;
if(d[k].dat[0]){
if(d[lc].dat2[0] == l1) d[k].dat2[0] = d[lc].dat2[0] + 1 + d[rc].dat2[0];
else d[k].dat2[0] = d[lc].dat2[0];
if(d[rc].dat2[1] == l2) d[k].dat2[1] = d[rc].dat2[1] + 1 + d[lc].dat2[1];
else d[k].dat2[1] = d[rc].dat2[1];
}else{
d[k].dat2[0] = d[lc].dat2[0];
d[k].dat2[1] = d[rc].dat2[1];
}
if(d[k].dat[1]){
if(d[lc].dat2[2] == l1) d[k].dat2[2] = d[lc].dat2[2] + 1 + d[rc].dat2[2];
else d[k].dat2[2] = d[lc].dat2[2];
if(d[rc].dat2[3] == l2) d[k].dat2[3] = d[rc].dat2[3] + 1 + d[lc].dat2[3];
else d[k].dat2[3] = d[rc].dat2[3];
}else{
d[k].dat2[0] = d[lc].dat2[0];
d[k].dat2[1] = d[rc].dat2[1];
}
if(d[lc].dat3[0] == 0) d[k].dat3[0] = d[rc].dat3[0];
else d[k].dat3[0] = d[lc].dat3[0];
if(d[rc].dat3[1] == 0) d[k].dat3[1] = d[lc].dat3[1];
else d[k].dat3[1] = d[rc].dat3[1];
d[k].dat1[0] = d[k].dat1[1] = 0;
if(d[k].dat3[0] == 0){
if(d[k].dat[0] == 0 || d[k].dat[1] == 0){
d[k].dat4 = d[lc].dat4 + d[rc].dat4 + 1;
}else d[k].dat4 = d[lc].dat4 + d[rc].dat4 + 2;
}else{
if(d[k].dat3[0] == d[lc].dat3[0]){
if(d[lc].dat1[0]) d[k].dat1[0] = 1;
}else{
if(d[rc].dat1[0]) d[k].dat1[0] = 1;
}
if(d[k].dat3[1] == d[rc].dat3[1]){
if(d[rc].dat1[1]) d[k].dat1[1] = 1;
}else{
if(d[lc].dat1[1]) d[k].dat1[1] = 1;
}
if(d[k].dat[0] == 0 || d[k].dat[1] == 0){
d[k].dat4 = d[lc].dat4 + d[rc].dat4 + 1;
}else if(d[k].dat3[0] != d[lc].dat3[0] || d[k].dat3[1] != d[rc].dat3[1]){
d[k].dat4 = d[lc].dat4 + d[rc].dat4 + 2;
}else if(d[lc].r - d[lc].dat2[1] > d[lc].dat3[1] || d[lc].r - d[lc].dat2[3] > d[lc].dat3[1] || d[rc].l + d[rc].dat2[0] < d[rc].dat3[0] || d[rc].dat2[2] + d[rc].l < d[rc].dat3[0] ){
d[k].dat4 = d[lc].dat4 + d[rc].dat4 + 2;
}else{
int tmp = d[lc].dat4 + d[rc].dat4 - (d[lc].r - d[lc].dat3[1]) * 2 - (d[rc].dat3[0] - d[rc].l) * 2;
if(!d[lc].dat1[1]) tmp--;
if(!d[rc].dat1[0]) tmp--;
d[k].dat4 = tmp;
if(d[lc].dat3[0] == d[lc].dat3[1]) d[k].dat1[0] = 1;
if(d[rc].dat3[0] == d[rc].dat3[1]) d[k].dat1[1] = 1;
}
}
//printf("%d %d %d %d %d %d %d %d\n", k, d[k].l, d[k].r, d[k].dat[0], d[k].dat[1], d[k].dat3[0], d[k].dat3[1], d[k].dat4);
}

void build(int k, int l, int r){
d[k].l = l; d[k].r = r;
if(l == r){
d[k].dat[0] = d[k].dat[1] = 0;
d[k].dat1[0] = d[k].dat1[1] = 0;
d[k].dat2[0] = d[k].dat2[1] = d[k].dat2[2] = d[k].dat2[3] = 0;
d[k].dat3[0] = d[k].dat3[1] = l;
d[k].dat4 = 1;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
d[k].dat[0] = d[k].dat[1] = 1;
pushup(k);
}

void modify1(int k, int pos, int opt){
if(d[k].l == d[k].r){
if(d[k].dat3[0] == 0 && opt == 1){
d[k].dat[0] = d[k].dat[1] = 0;
d[k].dat1[0] = d[k].dat1[1] = 0;
d[k].dat2[0] = d[k].dat2[1] = d[k].dat2[2] = d[k].dat2[3] = 0;
d[k].dat3[0] = d[k].dat3[1] = d[k].l;
d[k].dat4 = 1;
return;
}else if(d[k].dat3[0] != 0 && opt == 2){
d[k].dat[0] = d[k].dat[1] = 0;
d[k].dat1[0] = d[k].dat1[1] = 0;
d[k].dat2[0] = d[k].dat2[1] = d[k].dat2[2] = d[k].dat2[3] = 0;
d[k].dat3[0] = d[k].dat3[1] = 0;
d[k].dat4 = 0;
return;
}
}
int mid = (d[k].l + d[k].r) >> 1;
if(pos <= mid) modify1(lc, pos, opt);
if(pos > mid) modify1(rc, pos, opt);
pushup(k);
}

void modify2(int k, int pos, int x, int opt){
if(d[lc].r == pos){
if(d[k].dat[x] == 0 && opt == 1){
d[k].dat[x] = 1;
}else if(d[k].dat[x] == 1 && opt == 2){
d[k].dat[x] = 0;
}
pushup(k);
return;
}
int mid = d[lc].r;
if(pos < mid) modify2(lc, pos, x, opt);
if(pos > mid) modify2(rc, pos, x, opt);
pushup(k);
}

}Seg;
void Solve(){
scanf("%d%d", &n, &m);
Seg.build(1, 1, n);
//printf("%d\n", Seg.d[1].dat4);
for(int i = 1; i <= m; i ++){
int opt, a1, b1, a2, b2;
scanf("%d%d%d%d%d", &opt, &a1, &b1, &a2, &b2);
if(a1 != a2){
Seg.modify1(1, b1, opt);
}else{
if(b1 > b2) swap(b1, b2);
Seg.modify2(1, b1, a1, opt);
}
printf("%d\n", Seg.d[1].dat4);
}
}

int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
Solve();
}

return 0;
}


C - Empty Convex Polygons

题意:定义 「Ass♂Hole」 「凸洞」(Convex Hole)为一个凸多边形,顶点为给定的点,内部不包含其他给定的点。求出面积最大的「凸洞」。

N≤1000 。

题解:我不会计算几何(要不然就不会做那个毒瘤数据结构了)。坑。

D - Defense of the Ancients

坑。

E - Five-round Show Hand

坑。

F - Heron and His Triangle

题意:定义「Heron 三角形」是一种特殊的三角形,它们的三边长分别是 t−1,t,t+1 (t∈Z)。给定 N 求出 t ≥N 的面积为整数的 t最小的「Heron 三角形」。

题解:看题目名先想到 Heron 公式:

S=p(p−a)(p−b)(p−c)−−−−−−−−−−−−−−−−−√=a+b+c2⋅b+c−a2⋅a+c−b2⋅a+b−c2−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√=3t⋅t⋅(t+2)⋅(t−2)16−−−−−−−−−−−−−−−−−√=143t2⋅(t2−4)−−−−−−−−−−−√=143t4−12t2−−−−−−−−√

即 3t4−12t2 是 4 的倍数的平方。

然后后面的不会证了。。于是打表

t1=4,t2=14,t3=52,t4=194,t5=724,…

找到规律:

tn=4tn−1−tn−2

可以用数学归纳法证明(然而还是不能推出来)

需要高精(不能交 Python)

Hash_Table‘s Code:

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<"="<<x<<endl
const int maxn = 501;

struct Bign
{
int a[maxn];int len;
Bign()
{
memset(a,0,sizeof a);
len=1;
}
Bign operator = (long long num)
{
memset(a,0,sizeof a);
len=0;
while(num)
{
len++;
a[len]=num%10;
num/=10;
}
return *this;
}
Bign operator + (const long long num)
{
Bign c;
memcpy(c.a,a,sizeof a);
c.len=len;c.a[1]+=num;
int i=1;
while(c.a[i]>=10)
{
c.a[i+1]+=(c.a[i]/10);
c.a[i]%=10;
i++;
}
if(i>c.len) c.len=i;
return c;
}
Bign operator + (const Bign &x)
{
Bign c;
c.len=max(len,x.len);
for(int i=1;i<=c.len;i++)
{
c.a[i]+=a[i]+x.a[i];
c.a[i+1]+=(c.a[i]/10);
c.a[i]%=10;
}
if(c.a[c.len+1]) c.len++;
return c;
}
Bign operator * (const long long num)
{
Bign c;
memcpy(c.a,a,sizeof a);
c.len=len;
for(int i=1;i<=len;i++) c.a[i]*=num;
for(int i=1;i<=len;i++)
{
c.a[i+1]+=(c.a[i]/10);
c.a[i]%=10;
}
while(c.a[c.len+1])
{
c.len++;
c.a[c.len+1]+=(c.a[c.len]/10);
c.a[c.len]%=10;
}
return c;
}
Bign operator * (const Bign &x)
{
Bign c;
c.len=len+x.len;
for(int i=1;i<=len;i++)
for(int j=1;j<=x.len;j++)
c.a[i+j-1]+=a[i]*x.a[j];
for(int i=1;i<c.len;i++)
{
c.a[i+1]+=(c.a[i]/10);
c.a[i]%=10;
}
while(c.a[c.len]==0&&c.len>1) c.len--;
return c;
}
Bign operator - (const Bign &x)
{
Bign c;
c.len=len;
for(int i=1;i<=len;i++)
{
if(a[i]<x.a[i])
{
a[i+1]--;
a[i]+=10;
}
c.a[i]=a[i]-x.a[i];
}
while(c.a[c.len]==0&&c.len>1) c.len--;
return c;
}
bool operator < (const Bign &x) const
{
if(len!=x.len) return len<x.len;
for(int i=len;i>=1;i--)
if(a[i]!=x.a[i])
return a[i]<x.a[i];
return false;
}
bool operator == (const Bign &x) const
{
if(len!=x.len) return false;
for(int i=1;i<=len;i++)
if(a[i]!=x.a[i])
return false;
return true;
}
bool operator > (const Bign &x) const
{
return (!(*this<x))&&(!(*this==x));
}
bool operator >= (const Bign &x) const
{
return !(*this<x);
}
bool operator <= (const Bign &x) const
{
return !(*this>x);
}
bool operator != (const Bign &x) const
{
return !(*this==x);
}
void read()
{
char c[maxn];
scanf("%s",c+1);
int l=strlen(c+1);
for(int i=l;i>=1;i--)
a[l-i+1]=c[i]-'0';
len=l;
}
void print()
{
for(int i=len;i>=1;i--) printf("%d",a[i]);
}
void init_max()
{
for(int i=1;i<=500;i++) a[i]=9;
len=10000;
}
}lim,n,f[maxn];

int T;

int main()
{
lim=1;for(int i=1;i<=31;i++) lim=lim*10;
f[0]=2;f[1]=4;
for(int i=2;;i++)
{
f[i]=f[i-1]*4-f[i-2];
if(f[i]>=lim)
{
break;
}
}
scanf("%d",&T);
while(T--)
{
n.read();
for(int i=1;;i++) if(f[i]>=n)
{
f[i].print();
puts("");
break;
}
}
return 0;
}


G - Infinite Fraction Path

题意:给定一个长为 n 的正整数序列 {Di} (编号 0…n−1)每一位都是[0,9] 之间的整数。第 i 个位置向第 (i2+1)modn 个位置连边。显然从一个位置出发,不断沿着该位置所连出去的边移动,最终会形成无限循环 。现在要从一个点开始走 n 步,每一步把该位置的数字放在一个序列后面。求从哪里开始走形成的序列字典序最大。

N≤150000 。

题解:显然本题没有什么直观的做法。但是考虑一下 (i2+1)modn ,从每个点出发,重复几次走到的不重复的点数量级会下降很快。大约 105 级别的序列 3∼5 次就会下降到几百以内。所以考虑暴力BFS,能够通过此题。

My Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

char ch[150005];
int a[150005];
int n;
int ans[150005];
int vis[150005];
int stk[150005], tp;
int stk2[150005], tp2;
void bfs(){
for(int i = 0; i < n; i ++){
stk[++tp] = i;
}
for(int I = 1; I <= n; I ++){
int mx = 0;
for(int i = 1; i <= tp; i ++){
mx = max(mx, a[stk[i]]);
}
ans[I] = mx;
tp2 = 0;
for(int i = 1; i <= tp; i ++){
if(a[stk[i]] == mx){
stk2[++tp2] = stk[i];
}
}
tp = 0;
for(int i = 1; i <= tp2; i ++){
int to = (1ll * stk2[i] * stk2[i] + 1) % n;
if(!vis[to]){
vis[to] = 1;
stk[++tp] = to;
}
}
for(int i = 1; i <= tp2; i ++){
int to = (1ll * stk2[i] * stk2[i] + 1) % n;
vis[to] = 0;
}
}

}
int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
scanf("%d", &n);
scanf("%s", ch);
for(int i = 0; i < n; i ++) a[i] = ch[i] - '0';
bfs();
printf("Case #%d: ", I);
for(int i = 1; i <= n; i ++){
printf("%d", ans[i]);
}
printf("\n");
}

return 0;
}


H - Legends of the Three Kingdoms

坑。

I - Little Boxes

题意:给定四个非负整数 a,b,c,d,每个数小于 262 ,求他们的和。

题解:有坑。他们的和可能等于 264 ,此时
unsigned long long
存不下,特判一下既可。

My Code:

#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ll;

ll a, b, c, d;

int main(){
int T;
scanf("%d", &T);
while(T--){
cin >> a >> b >> c >> d;
ll sum = a + b + c + d;
if(sum == 0 && a != 0){
cout << "18446744073709551616" << endl;
}else cout << sum << endl;

}

return 0;
}


J - New Self-describing Sequence

坑。

K - Rabbits

题意:有 N(N≥3) 只兔子在数轴上玩♂游♂戏。每次一只兔子跳到两只兔子之间。兔子的坐标只能是整数,且一个点不能有两只或以上兔子。问它们最多能跳几次。

题解:贪心。只有第一只兔子和最后一只兔子可能浪费掉一部分空位,贪心选择最小的放弃,然后填满所有空位即可。

My Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int n;
int a[100005];
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
int sum = 0;
for(int i = 1; i < n; i ++) sum = sum + a[i + 1] - a[i] - 1;
int t1 = a[2] - a[1] - 1, t2 = a
- a[n - 1] - 1;
sum = sum - min(t1, t2);
printf("%d\n", sum);
}

return 0;
}


L - Tree

题意:一棵树有 N 个节点,要把每个节点染成 K 种颜色之一。定义 Ei 是连接所有第 i 种颜色的点的边集,最大化 ∣∣⋂ni=1Ei∣∣ 。

题解:把边放到儿子节点上。一个节点对应的边在最后的集合里,当且仅当该点为根的子树有所有 k 种颜色,其他的点也有所有 k 种颜色。答案就是 ∑ni=1[min(size[i],n−size[i])≥k] 。

My Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

struct edge{
int to, nxt;
}e[100005];

int h[100005], cnt;

void addedge(int x, int y){
cnt++; e[cnt].to = y; e[cnt].nxt = h[x]; h[x] = cnt;
cnt++; e[cnt].to = x; e[cnt].nxt = h[y]; h[y] = cnt;
}

int siz[100005];

void dfs(int x, int f){
siz[x] = 1;
for(int i = h[x]; i; i = e[i].nxt){
if(e[i].to == f) continue;
dfs(e[i].to, x);
siz[x] += siz[e[i].to];
}
}

int n, k;
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++) h[i] = 0;
cnt = 0;
for(int i = 1; i < n; i ++){
int x, y;
scanf("%d%d", &x, &y);
addedge(x, y);
}
dfs(1, 0);
int ans = 0;
for(int i = 1; i <= n; i ++){
if(min(siz[i], n - siz[i]) >= k) ans++;
}
printf("%d\n", ans);

}

return 0;
}


M - Wandering Robots

题意:一个机器人在一个 N×N 的网格里。左上角为 (0,0), 右下角为 (N−1,N−1) 。有一些格子是障碍,不能通过。机器人初始在左上角,每次等概率地向周围格子随机移动。问移动 ∞ 次后停在右下部分 (点的坐标 (x,y) 满足 x+y≥N−1)的概率。

保证能到达右下部分。

N≤10000。

题解:

分析样例后发现一个结论:每个格子有一个大小为 (相邻的非障碍格子数 +1) 的权值。障碍格子的权值为 0 。答案就是右下部分格子权值占所有格子权值的比例。

因为走 ∞ 步,只有长者能够验证结论对不对。

My Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};

ll n;int k;
map<P, int> mp;

ll gcd(ll a, ll b){
return b == 0 ? a : gcd(b, a % b);
}

ll cal(int x, int y){
ll ret = 5;
if(x == 0) ret--; if(x == n - 1) ret--;
if(y == 0) ret--; if(y == n - 1) ret--;
return ret;
}
int main(){
int T;
scanf("%d", &T);
for(int I = 1; I <= T; I ++){
scanf("%lld%d", &n, &k);
mp.clear();

ll fm = 5 * n * n - 4 * n;
ll fz = 5 * n * (n + 1) / 2 - n - n - 2;
for(int i = 1; i <= k; i ++){
int x, y;
scanf("%d%d", &x, &y);
mp[make_pair(x, y)] += 114514;
for(int j = 0; j < 4; j ++){
int xx = x + dx[j], yy = y + dy[j];
if(xx < 0 || xx >= n || yy < 0 || yy >= n) continue;
mp[make_pair(xx, yy)] ++;
}
}
for(auto it:mp){
int x = it.first.first, y = it.first.second;
ll res = 0;
if(it.second > 5) res = cal(x, y); else res = it.second;
if(x + y >= n - 1) fz -= res; fm -= res;
}
ll gc = gcd(fz, fm);
fz /= gc; fm /= gc;
printf("Case #%d: %lld/%lld\n", I, fz, fm);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM-ICPC
相关文章推荐