NWERC 2015(2020.7.1训练赛)

比赛网址
写这个主要是来监督自己补题的emmm

文章目录

  • I Identifying Map Tiles
  • E Elementary Math
  • K Kitchen Combinatorics
  • J Jumbled Communication
  • A - Assigning Workstations
  • D.Debugging
  • G.Guessing Camels(cdq分治,待学待补)
  • C.Cleaning Pipes

I Identifying Map Tiles

题目描述
根据题目所给的字符串所相对应的图表,输出它的地图的level和它所对应的坐标位置
思路
挺好解决的,字符串长度就是图表level,后面的坐标具体位置也很好理解,每次都是由一个格子话分成四个格子,所以对于字符串每个字符的划分都可以确定这个字符串所对应的坐标位置,然后模拟一下即可
代码

#include
using namespace std;

void solve() {
    string s;
    cin >> s;
    printf("%d ", s.size());
    int xl = 1, xr = pow(2, s.size()), yl = 1, yr = pow(2, s.size());
    for(int i = 0; i < s.size(); i++) {
        int x = xl + xr >> 1;
        int y = yl + yr >> 1;
        if(s[i] == '0') {
            xr = x;
            yr = y;
        }
        else if(s[i] == '1') {
            xl = x + 1;
            yr = y;
        }
        else if(s[i] == '2') {
            xr = x;
            yl = y + 1;
        }
        else if(s[i] == '3') {
            xl = x + 1;
            yl = y + 1;
        }
        //printf("%d %d %d %d\n", xl, xr, yl, yr);
    }
    printf("%d %d\n", xr - 1, yr - 1);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

E Elementary Math

题目描述
给定n对数字,每对数字可以进行 ‘+’, ‘-’, ’ * ’ 三种操作,最后判断是否有n个不相同的答案,若有答案,则输出所对应的解,没有n个解就输出impossible。
思路
把题目转化成二分图匹配来看,左边n为个对数,右边为所有可能的解,然后一一匹配即可,差不多就变成了二分图匹配的裸题
代码

#include
using namespace std;

typedef long long LL;
typedef pair<LL, LL> PII;
const int N = 1e4 + 10;
struct Edge {
    int to, next;
}e[N];
int head[N], idx;
bool st[N];
int match[N];
map<LL, int> pos;
vector<PII> cal;
LL tt[N];
int Map[N];

void add(int u, int v) {
    e[++idx].to = v;
    e[idx].next = head[u];
    head[u] = idx;
}

bool find(int u) {
    for(int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if(!st[v]) {
            st[v] = 1;
            if(!match[v] || find(match[v])) {
                match[v] = u;
                Map[u] = v;
                return true;
            }
        }
    }
    return false;
}

void solve() {
    int n;
    scanf("%d", &n);
    int x = 0;
    for(int i = 1; i <= n; i++) {
        LL a, b;
        scanf("%lld%lld", &a, &b);
        if(!pos[a + b]) pos[a + b] = ++x, tt[x] = a + b;
        if(!pos[a * b]) pos[a * b] = ++x, tt[x] = a * b;
        if(!pos[a - b]) pos[a - b] = ++x, tt[x] = a - b;
        add(i, pos[a + b]);
        add(i, pos[a * b]);
        add(i, pos[a - b]);
        cal.push_back({a, b});
    }

    vector<PII>::iterator it;
    x = 0;
    int res = 0;
    for(int i = 1; i <= n; i++) {
        memset(st, 0, sizeof st);
        if(find(i)) res++;
    }

    if(res < n) puts("impossible");
    else {
        for(int i = 1; i <= n; i++) {
            LL num = tt[Map[i]];
            LL a = cal[i - 1].first, b = cal[i - 1].second;
            //printf("%lld %lld %d\n", a, b, match[i]);
            if(a + b == num) printf("%lld + %lld = %lld\n", a, b, num);
            else if(a * b == num) printf("%lld * %lld = %lld\n", a, b, num);
            else if(a - b == num) printf("%lld - %lld = %lld\n", a, b, num);
        }
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

K Kitchen Combinatorics

题目描述
有r种食材,有三种类型的菜,每种类型的菜可能有不同种食材,同时有n个限制条件,最后问有多少种搭配方式。
思路
根据题目暴力模拟就行了,感觉就被越界范围给坑了一下吧,乘法判断以后都先除一下再进行操作了。或者使用__int128了
代码

#include
using namespace std;

typedef long long LL;
typedef tuple<int, int, int> TPL;
typedef pair<int, int> PII;
const int N = 1010, M = 110, W = 20010;
LL A[N];
vector<int> e[M];
bool st[M][M];
int u[W], v[W], w[W];
map<set<int>, int> vis;

bool check(int u, int v, int w) {
    if(st[u][v] || st[u][w] || st[v][w]) return false;
    return true;
}

void solve() {
    int r, s, m, d, n;
    scanf("%d%d%d%d%d", &r, &s, &m, &d, &n);
    for(int i = 1; i <= r; i++) {
        scanf("%lld", &A[i]);
    }
    for(int i = 1; i <= s + m + d; i++) {
        int k;
        scanf("%d", &k);
        while(k--) {
            int x;
            scanf("%d", &x);
            e[i].push_back(x);
        }
    }

    for(int i = 1; i <= n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        st[u][v] = 1;
        st[v][u] = 1;
    }

    int x = 0;
    for(int i = 1; i <= s; i++) {
        for(int j = s + 1; j <= s + m; j++) {
            for(int k = s + m + 1; k <= s + m + d; k++) {
                if(check(i, j, k)) {
                    ++x;
                    u[x] = i;
                    v[x] = j;
                    w[x] = k;
                }
            }
        }
    }

    LL res = 0;
    set<int>::iterator it;
    for(int i = 1; i <= x; i++) {
        int a = u[i], b = v[i], c = w[i];
        set<int> s;
        for(int j = 0; j < e[a].size(); j++) {
            s.insert(e[a][j]);
        }
        for(int j = 0; j < e[b].size(); j++) {
            s.insert(e[b][j]);
        }
        for(int j = 0; j < e[c].size(); j++) {
            s.insert(e[c][j]);
        }
        LL cnt = 1;

        for(it = s.begin(); it != s.end(); it++) {
            int num = *it;
            if(cnt > (LL)1e18 / A[num]) {
                puts("too many");
                return;
            }
            cnt *= A[num];
        }

        if(res + cnt > (LL)1e18 || res < 0) {
            puts("too many");
            return;
        }
        res += cnt;
    }
    printf("%lld\n", res);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

J Jumbled Communication

题目描述
给定一个数字num, 求满足式子 num = x ^ (x << 1) 的x
思路
x 和 x <<= 1 斜对角数字一定相等,按照num模拟一下即可
代码

#include
using namespace std;

typedef long long LL;
vector<int> s1, s2;

void solve() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        int num;
        scanf("%d", &num);
        if(num == 0) {
            printf("0 ");
            continue;
        }
        int x = 0, y = 1;
        s2.push_back(0);
        int k = 8;
        while(k--) {
            if(num & 1) {
                if(s2[x] == 1) s1.push_back(0);
                else if(s2[x] == 0) s1.push_back(1);
                s2.push_back(s1[x]);
            }
            else {
                s2.push_back(s2[x]);
                s1.push_back(s2[x]);
            }
            x++; y++;
            num >>= 1;
        }
        int tmp = 1, res = 0;
        for(int j = 0; j < s1.size(); j++) {
            if(s1[j] == 1) res += tmp;
            tmp <<= 1;
        }
        printf("%d ", res);
        s1.clear(); s2.clear();
    }
}

int main() {
	//freopen("in.txt", "r", stdin);
	solve();
	return 0;
}

A - Assigning Workstations

题目大意
给定n个人的开始时间和工作时间长度,并给m分钟的机器待机时间(超过m分钟机器就关机,再工作就要重新开机)。问有多少个人不用重新开机
思路
贪心+优先队列,先排序每次开始工作的时间,然后将结束时间存到优先队列里面,每次判断队顶的元素是否符合要求就行了
如果出现wa14的情况,就可能和我一开始思考的一样了,用一个数组维护二分大小,但是此时存入的结束工作的值的大小不确定,不能维护一个稳定的递增或递减序列
代码

#include
using namespace std;

typedef long long LL;
const int N = 3e5 + 10;
struct node {
    LL x, y;
    friend bool operator < (node a, node b) {
        if(a.x == b.x) {
            return a.y < b.y;
        }
        return a.x < b.x;
    }
}a[N];
priority_queue<LL, vector<LL>, greater<LL> > q;

void solve() {
    LL n, m;
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%lld%lld", &a[i].x, &a[i].y);
        a[i].y += a[i].x;
    }
    sort(a + 1, a + 1 + n);
    int res = 0;
    for(int i = 1; i <= n; i++) {
        int start = a[i].x, stop = a[i].y;
        while(!q.empty() && q.top() <= start) {
            if(q.top() + m < start) q.pop();
            else {
                q.pop();
                res++;
                break;
            }
        }
        q.push(stop);
    }
    printf("%d\n", res);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

D.Debugging

题目描述
有n行代码,里面有一个错误,编译一次需要花费r分钟,printf一行需要花费p分钟,问最少需要几分钟就可以锁定这个错误
思路
记忆化搜索。dp[n]表示代码数为n行时所需要解决问题的最少代价。dp[n] = min( x * p + dp[ ceil(n / (x + 1))] + r) (x为在n行中输出的printf数量,1 <= x < n)
代码

#include
using namespace std;

typedef long long LL;
const int N = 1e6 + 10;
const LL inf = 1e18;
LL n, r, p;
LL dp[N];

LL cacl(LL n) {
    if(n == 1) return 0;
    if(dp[n]) return dp[n];
    LL res = (n - 1) * p + r;
    for(int i = 2; i <= n; i++) {
        res = min(res, cacl(n / i + (n % i != 0)) + (i - 1) * p + r);
    }
    return dp[n] = res;
}

void solve() {
    scanf("%lld%lld%lld", &n, &r, &p);
    cacl(n);
    printf("%lld\n", dp[n]);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

G.Guessing Camels(cdq分治,待学待补)

C.Cleaning Pipes

题目描述
给定w个点,再给定n条线,两线可能相交(同一起点不算相交),判断从其中一条直线的入口进入是否会和另外一条从其入口进入发生碰撞
思路
计算几何+二分图判断,如果线A和线B有交点,要么从A进入,要么从B进入。如果选择A线,则B线上若再有进入就会发生碰撞,所以一旦出现了必须由从B线进入的情况。就impossible。可以用构建图以后判定二分图
(判定两直线是否相交是从某博客上偷来的板子emmm)
代码

#include
using namespace std;

typedef long long LL;

const int N = 1005;
struct Point {
    int x, y;
}w[N], p[N];
int st[N];

// 判断两线段是否相交
int direction (Point p0, Point p1, Point p2) {
    return ((p2.x - p0.x)*(p1.y - p0.y) - (p1.x - p0.x)*(p2.y - p0.y));
}

bool on_segment (Point p0, Point p1, Point p2) {
    int minx, maxx, miny, maxy;
    minx = min(p0.x, p1.x);
    maxx = max(p0.x, p1.x);
    miny = min(p0.y, p1.y);
    maxy = max(p0.y, p1.y);
    if (p2.x >= minx && p2.x <= maxx && p2.y >= miny && p2.y <= maxy)
        return true;
    else
        return false;
}

bool segments_intersect (Point p1, Point p2, Point p3, Point p4) {
    int d1, d2, d3, d4;
    d1 = direction(p3, p4, p1);
    d2 = direction(p3, p4, p2);
    d3 = direction(p1, p2, p3);
    d4 = direction(p1, p2, p4);
    if (((d1 < 0 && d2 > 0) || (d1 > 0 && d2 < 0)) && ((d3 < 0 && d4 > 0) || (d3 > 0 && d4 < 0)))
        return true;
    else if (d1 == 0 && on_segment(p3, p4, p1))
        return true;
    else if (d2 == 0 && on_segment(p3, p4, p2))
        return true;
    else if (d3 == 0 && on_segment(p1, p2, p3))
        return true;
    else if (d4 == 0 && on_segment(p1, p2, p4))
        return true;
    else
        return false;
}

// 染色法判定二分图
vector<int> e[N];
bool flag;
int color[N];

void dfs(int u, int st) {
    for(int i = 0; i < e[u].size(); i++) {
        int v = e[u][i];
        if(!color[v]) {
            color[v] = -st;
            dfs(v, -st);
        }
        else {
            if(color[v] == st) flag = 0;
        }
    }
}

void solve() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d", &w[i].x, &w[i].y);
    }
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &st[i], &p[i].x, &p[i].y);
    }

    for(int i = 1; i <= m; i++) {
        for(int j = i + 1; j <= m; j++) {
            if(st[i] == st[j]) continue;
            if(segments_intersect(w[st[i]], p[i], w[st[j]], p[j])) {
                e[i].push_back(j);
                e[j].push_back(i);
            }
        }
    }

    flag = 1;
    for(int i = 1; i <= m; i++) {
        if(!color[i]) {
            color[i] = 1;
            dfs(i, 1);
        }
    }
    if(flag) puts("possible");
    else puts("impossible");

}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

你可能感兴趣的:(Gym,训练日志)