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

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

比赛结果:
这里写图片描述

A - BBP Formula

坑。

B - Bridge

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

N,M2×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)为一个凸多边形,顶点为给定的点,内部不包含其他给定的点。求出面积最大的「凸洞」。

N1000

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

D - Defense of the Ancients

坑。

E - Five-round Show Hand

坑。

F - Heron and His Triangle

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

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

S=p(pa)(pb)(pc)=a+b+c2b+ca2a+cb2a+bc2=3tt(t+2)(t2)16=143t2(t24)=143t412t2

3t412t2 4 的倍数的平方。
然后后面的不会证了。。于是打表
t1=4,t2=14,t3=52,t4=194,t5=724,

找到规律:

tn=4tn1tn2

可以用数学归纳法证明(然而还是不能推出来)
需要高精(不能交 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} (编号 0n1 )每一位都是 [0,9] 之间的整数。第 i 个位置向第 (i2+1)modn 个位置连边。显然从一个位置出发,不断沿着该位置所连出去的边移动,最终会形成无限循环 。现在要从一个点开始走 n 步,每一步把该位置的数字放在一个序列后面。求从哪里开始走形成的序列字典序最大。

N150000

题解:显然本题没有什么直观的做法。但是考虑一下 (i2+1)modn ,从每个点出发,重复几次走到的不重复的点数量级会下降很快。大约 105 级别的序列 35 次就会下降到几百以内。所以考虑暴力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(N3) 只兔子在数轴上玩♂游♂戏。每次一只兔子跳到两只兔子之间。兔子的坐标只能是整数,且一个点不能有两只或以上兔子。问它们最多能跳几次。

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

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[n] - 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],nsize[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) , 右下角为 (N1,N1) 。有一些格子是障碍,不能通过。机器人初始在左上角,每次等概率地向周围格子随机移动。问移动 次后停在右下部分 (点的坐标 (x,y) 满足 x+yN1 )的概率。
保证能到达右下部分。

N10000

题解:
分析样例后发现一个结论:每个格子有一个大小为 (相邻的非障碍格子数 +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;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM-ICPC(国际大学生程序设计竞赛)是一项面向大学生的计算机编程竞赛,涉及算法和数据结构等领域。在比赛中,选手需要解决一系列编程问题,使用合适的算法和数据结构来实现正确和高效的解决方案。 对于整理ACM-ICPC模板,以下是一些建议: 1. 了解比赛要求:首先,你需要了解ACM-ICPC比赛的具体要求和规则。这包括了解比赛所涉及的算法和数据结构,以及题目的类型和难度等。 2. 收集资料:收集与ACM-ICPC相关的资料,包括经典算法和数据结构的实现代码、常见问题的解题思路等。可以参考教材、博客、论文等资源。 3. 整理模板:将收集到的资料整理成模板。可以按照算法和数据结构的分类进行整理,例如排序算法、图算法、字符串算法等。对每个模板,添加必要的注释和示例代码,以便理解和使用。 4. 测试代码:对每个模板编写测试代码,确保它们的正确性和可靠性。可以使用已知的测试用例或自行设计测试用例。 5. 更新与扩充:定期更新和扩充模板,以适应ACM-ICPC比赛中新出现的算法和数据结构。同时,根据自己的经验和理解,对模板进行优化和改进。 6. 练习和复习:在比赛之前,利用整理好的模板进行练习和复习。尝试解决一些经典问题,使用模板中的算法和数据结构进行实现,并进行优化。 希望这些建议对你整理ACM-ICPC模板有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值