Technocup 2017 - Elimination Round 1 (Unofficially Open for Everyone, Rated for Div. 2) 解题报告

A. Transformation: from A to B

题意:

一个数a,两种操作,一是把a*2,二是把a*10+1,问把a变到b是否有解和最小次数。

思路:

两种操作都是指数级别的,爆搜即可。

//
//  main.cpp
//  727A
//
//  Created by 翅膀 on 16/10/21.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <map>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
map<ll, ll> pre;
void bfs(ll a, ll b) {
    queue<ll>q;
    q.push(a); pre[a] = -1;
    while(!q.empty()) {
        ll u = q.front(); q.pop();
        if(u == b) return;
        ll v1 = u*2;
        if(v1 <= b && pre.find(v1) == pre.end()) {
            q.push(v1); pre[v1] = u;
        }
        ll v2 = u*10+1;
        if(v2 <= b && pre.find(v2) == pre.end()) {
            q.push(v2); pre[v2] = u;
        }
    }
}
int main(int argc, const char * argv[]) {
    ll a, b;
    cin >> a >> b;
    bfs(a, b);
    if(pre.find(b) == pre.end()) return 0*puts("NO");
    stack<ll>ans;
    while(b != -1) {
        ans.push(b);
        b = pre[b];
    }
    puts("YES");
    printf("%lld\n", (ll)ans.size());
    while(!ans.empty()) {
        printf("%lld ", ans.top()); ans.pop();
    }
    return 0;
}

B. Bill Total Value

题意:

给一串字符串,分析里面的数字出来求和,数字是每三位有逗号,如果有小数则固定是三位小数。

思路:

按题意模拟即可,码力和提交次数成反比。

//
//  main.cpp
//  727B
//
//  Created by 翅膀 on 16/10/25.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
string s;
vector<string>ss;
double check(const string& x) {
    double res = 0;
    if(x.size() < 3) {
        for(char c : x) {
            res = res*10+(int)c-'0';
        }
        return res;
    }
    if(x[x.size()-3] == '.') {
        for(int i = 0; i < x.size()-3; ++i) {
            if(x[i] == '.') continue;
            res = res*10+(int)x[i]-'0';
        }
        res += ((int)x[x.size()-2]-'0')/10.0;
        res += ((int)x[x.size()-1]-'0')/100.0;
        return res;
    }
    else {
        for(int i = 0; i < x.size(); ++i) {
            if(x[i] == '.') continue;
            res = res*10+(int)x[i]-'0';
        }
        return res;
    }
}
int main(int argc, const char * argv[]) {
    cin >> s;
    for(int i = 0; i < s.size(); ++i) {
        if(s[i] >= '0' && s[i] <= '9') {
            for(int j = i; j < s.size(); ++j) {
                if(j == s.size()-1) {
                    ss.emplace_back(s.substr(i, j-i+1));
                    i = j;
                    continue;
                }
                if(s[j] >= 'a' && s[j] <= 'z') {
                    ss.emplace_back(s.substr(i, j-i));
                    i = j-1;
                    break;
                }
            }
        }
    }
    double ans = 0;
    for(auto& x : ss) {
        ans += check(x);
    }
    char sans[5000];
    sprintf(sans, "%.2f", ans);
    int len = (int)strlen(sans);
    int cnt = (len-3)%3;
    if(cnt == 0) cnt = 3;
    for(int i = 0; i < cnt; ++i) putchar(sans[i]);
    for(int i = cnt; i < len-3; i += 3) {
        if(cnt) putchar('.');
        putchar(sans[i]); putchar(sans[i+1]); putchar(sans[i+2]);
    }
    if(sans[len-1] != '0' || sans[len-2] != '0') {
        putchar('.');
        putchar(sans[len-2]);
        putchar(sans[len-1]);
    }
    puts("");
    return 0;
}

C. Guess the Array

题意:

交互题,数据是一个数组,你知道数组长度是n,然后你可以询问n次,每次询问任意ai+aj的和,输出整个数组。

思路:

先问a1+a2,a2+a3,a3+a1,然后就可以解出a1,a2,a3,后面的就直接出来了。

//
//  main.cpp
//  727C
//
//  Created by 翅膀 on 16/10/21.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int query(int i, int j) {
    printf("? %d %d\n", i, j);
    fflush(stdout);
    int res = 0;
    scanf("%d", &res);
    return res;
}
int a[5005];
int main(int argc, const char * argv[]) {
    int n;
    scanf("%d", &n);
    int a12 = query(1, 2);
    int a23 = query(2, 3);
    int a13 = query(1, 3);
    a[1] = (a13+a12-a23)/2;
    a[2] = a12-a[1];
    a[3] = a13-a[1];
    for(int i = 4; i <= n; ++i) {
        a[i] = query(1, i)-a[1];
    }
    printf("!");
    for(int i = 1; i <= n; ++i) {
        printf(" %d", a[i]);
    }
    puts("");
    fflush(stdout);
    return 0;
}

D. T-shirts Distribution

题意:

6种大小的衣服,各有若干件,然后每个人有1种或2种选择,每个人得到的衣服大小必须是他们选的,问有没有这种分配方案,可以就输出一种方案。

思路:

第一反应网络流,然后每个大小建一个点,每个人建一个点,按他们的选择连边,最后跑完最大流看是不是满流,然后根据流向输出方案。

//
//  main.cpp
//  727D
//
//  Created by 翅膀 on 16/10/21.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 150005;
const int M = 300005;
const int inf = ~0u>>2;
struct eg{
    int u, v, cap; //源点汇点流量
    eg(){}
    eg(int a, int b, int c){ u = a, v = b, cap = c; }
}edg[M*4]; //边数开大
int fir[N], nex[M*4], ecnt, s, t;
void add(int a, int b, int c){
    edg[ecnt] = eg(a, b, c);
    nex[ecnt] = fir[a], fir[a] = ecnt++;
    edg[ecnt] = eg(b, a, 0); //反向边
    nex[ecnt] = fir[b], fir[b] = ecnt++;
}
int lev[N], q[N*4], top, tail;
bool Bfs(int s, int t){
    memset(lev, -1, sizeof(lev));
    top = tail = 0;
    lev[s] = 0; q[tail++] = s;
    while( top < tail  ){
        int u = q[top++];
        if( u == t ) return 1;
        for(int k = fir[u]; k != -1; k = nex[k]){
            int v = edg[k].v;
            if( edg[k].cap && lev[v] == -1){
                lev[v] = lev[u] + 1;
                q[tail++] = v;
            }
        }
    }
    return 0;
}
int Dfs(int s, int t, int low){
    if( s == t ) return low;
    int a = 0, res = 0;
    for(int k = fir[s]; k != -1; k = nex[k]){
        int v = edg[k].v;
        if(edg[k].cap && lev[v] == lev[s] +1 ){
            a = Dfs(v, t, min(low - res, edg[k].cap) );
            edg[k].cap -= a;
            edg[k^1].cap += a;
            res += a;
            if(res == low) return res;
        }
    }
    if(res == 0) lev[s] = -1;
    return res;
}
int Dinic(int s, int t){
    int res = 0, minflow;
    while( Bfs(s, t) ){
        while( (minflow = Dfs(s, t, inf)) ) res += minflow;
    }
    return res;
}
int a[10];
char str[100];
inline int chos(const char& a, const char& b, const char& c) {
    if(a == 'S') return 1;
    else if(a == 'M') return 2;
    else if(a == 'L') return 3;
    else if(c == 'X') return 6;
    else if(b == 'X') return 5;
    else if(a == 'X') return 4;
    exit(-1);
}
int ans[100005];
inline void print(const int &i) {
    if(i == 1) puts("S");
    else if(i == 2) puts("M");
    else if(i == 3) puts("L");
    else if(i == 4) puts("XL");
    else if(i == 5) puts("XXL");
    else if(i == 6) puts("XXXL");
}
// 1~6 S~XXXL 7~12
vector<int>cnt[100];
int main(int argc, const char * argv[]) {
    memset(fir, -1, sizeof(fir));
    ecnt = 0;
    for(int i = 1; i <= 6; ++i){
        scanf("%d", a+i);
    }
    int q, sub = 0;
    scanf("%d", &q);
    s = 0, t = 100;
    for(int k = 7; k <= q+6; ++k) {
        memset(str, 0, sizeof(char)*10);
        scanf("%s", str);
        int pos = -1;
        for(int i = 0; str[i]; ++i) {
            if(str[i] == ',') {
                pos = i;
                break;
            }
        }
        if(pos == -1) {
            int tmp = chos(str[0], str[1], str[2]);
            if(a[tmp] == 0) return 0*puts("NO");
            a[tmp]--;
            ans[k] = tmp;
            sub++;
        }
        else {
            int a = chos(str[0], str[1], str[2]);
            int b = chos(str[pos+1], str[pos+2], str[pos+3]);
            cnt[a*6+b].push_back(k);
        }
    }
    for(int i = 1; i <= 6; ++i) {
        for(int j = 1; j <= 6; ++j) {
            if(i == j) continue;
            add(i, i*6+j,  a[i]);
            add(j, i*6+j, a[j]);
            add(i*6+j, t, (int)cnt[i*6+j].size());
        }
        add(s, i, a[i]);
    }
    for(int i = 1; i <= 6; ++i) add(s, i, a[i]);
    if(ecnt > M*4 || t > N) return -1;
    int maxflow = sub+Dinic(s, t);
    if(maxflow != q) return 0*puts("NO");
    for(int i = 1; i <= 6; ++i) {
        for(int j = 1; j <= 6; ++j) {
            for(int k = fir[i*6+j]; k != -1; k = nex[k]) {
                int v = edg[k].v;
                if(edg[k].cap && v <= 6 && v >= 1) {
                    for(int l = 0; l < edg[k].cap; ++l) {
                        if(cnt[i*6+j].empty()) return 0*puts("NO");
                        ans[cnt[i*6+j].back()] = v;
                        if(a[v] == 0) return 0*puts("NO");
                        a[v]--;
                        cnt[i*6+j].pop_back();
                    }
                }
            }
        }
    }
    puts("YES");
    for(int i = 6; i <= q+6; ++i) {
        print(ans[i]);
    }
    return 0;
}

E. Games on a CD

题意:

一个串长度为n*k,然后把它首尾接起来,再给你g(>=n)个长度为k的串,问你用这g个串能不能组成原来的串。

思路:

先把原串加倍,因为是环,所以枚举起点(最多k次)检查是不是可以组成。
我的做法是多项式hash,这样可以快速得到任意一段的hash值,g个串也hash,然后暴力匹配。推荐双hash,毕竟cf。
检查最多n次,枚举k次,总复杂度是O(n*k)的,先看出这个复杂度那就比较好做了。

//
//  main.cpp
//  727E
//
//  Created by 翅膀 on 16/10/22.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
typedef unsigned long long ull;
typedef pair<ull,ull> pii;
const ull mod1 = 982886081;
const ull mod2 = 889930273;
const ull s1 = 131;
const int N = 2e6+5;
ull pm1[N], pm2[N];
pii strhash[N];
void init(char *s, int len) {
    pm1[0] = pm2[0] = 1;
    for(int i = 1; i < N; ++i) {
        pm1[i] = pm1[i-1]*s1%mod1;
        pm2[i] = pm2[i-1]*s1%mod2;
    }
    strhash[0] = pii(s[0]*pm1[0], s[0]*pm2[0]);
    for(int i = 1; i < len; ++i) {
        strhash[i] = pii((strhash[i-1].first+s[i]*pm1[i])%mod1, (strhash[i-1].second+s[i]*pm2[i])%mod2);
    }
}
ull qpow(ull a, ull k, const ull& mod) {
    ull res = 1;
    while(k) {
        if(k&1) res = res*a%mod;
        a = a*a%mod;
        k >>= 1;
    }
    return res;
}
pii shash(int l, int r) {
    if(l == 0) return strhash[r];
    ull one = strhash[r].first;
    one = (one-strhash[l-1].first+mod1)%mod1;
    one = one*qpow(pm1[l], mod1-2, mod1)%mod1;
    ull two = strhash[r].second;
    two = (two-strhash[l-1].second+mod2)%mod2;
    two = two*qpow(pm2[l], mod2-2, mod2)%mod2;
    return make_pair(one, two);
}
pii ghash(char *s, int k) {
    ull r1 = 0, r2 = 0;
    for(int i = 0; i < k; ++i) {
        r1 = (r1+s[i]*pm1[i])%mod1;
        r2 = (r2+s[i]*pm2[i])%mod2;
    }
    return pii(r1, r2);
}
map<pii,int>mp;
bool used[N];
char s[N];
ull sh[N];
int main(int argc, const char * argv[]) {
    int n, k;
    scanf("%d%d", &n, &k);
    scanf("%s", s);
    int len = n*k;
    for(int i = 0; i < len; ++i) {
        s[i+len] = s[i];
    }
    init(s, len*2);
    int q;
    scanf("%d", &q);
    for(int i = 1; i <= q; ++i) {
        scanf("%s", s);
        pii res = ghash(s, k);
        mp[res] = i;
    }
    vector<int>ans;
    for(int i = 0; i < k; ++i) {
        for(int x : ans) used[x] = 0;
        ans.clear();
        for(int j = 0; j < n; ++j) {
            pii tar = shash(i+j*k, i+(j+1)*k-1);
            if(mp.find(tar) == mp.end() || used[mp[tar]]) break;
            int id = mp[tar];
            used[id] = 1;
            ans.push_back(id);
            if(j == n-1) {
                puts("YES");
                for(int x : ans) printf("%d ", x);
                return 0;
            }
        }
    }
    puts("NO");
    return 0;
}

F. Polycarp’s problems

题意:

n个数的数组a[i],m次询问,每次询问给出一个初始值x,然后x从a[1]累加到a[n],要求过程中x不能为负,问你最少要删除几个数。

思路:

考虑单次回答,贪心。当加到a[i]为负了,那么此时必然要删一个数,显然应该删a[1]~a[i]里面最小的没被删过的数,用个优先队列就可以nlogn单次回答,但是暴力被卡掉了。
换种做法,因为n<=750,所以最多有751个答案,而且初始值越大,答案越小,根据这种单调性就可以预处理答案为i时候的最小的初始值,回答的时候就可以直接二分了。

//
//  main.cpp
//  727F
//
//  Created by 翅膀 on 16/11/4.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
const int N = 800;
const ll inf = ~0ull>>3;
int n, m;
ll a[N];
ll ans[N];
ll check(ll x) {
    if(x < 0) return inf;
    int res = 0;
    priority_queue<ll, vector<ll>, greater<ll> >st;
    for(int i = 1; i <= n; ++i) {
        x += a[i];
        if(a[i] < 0) st.push(a[i]);
        if(x < 0) {
            x -= st.top(); st.pop();
            res++;
        }
    }
    return res;
}
ll solve(int x) {
    ll l = -inf, r = inf, ans = 0;
    while(l <= r) {
        ll mid = (l+r) >> 1;
        if(check(mid) <= x) r = mid-1, ans = mid;
        else l = mid+1;
    }
    if(ans < 0) ans = -inf;
    return ans;
}
int main(int argc, const char * argv[]) {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%lld", a+i);
    }
    int last = 0;
    for(int i = 0; i <= n; ++i) {
        ans[i] = solve(i);
        last = i;
        if(ans[i] < 0) break;
    }
    while(m--) {
        ll x;
        scanf("%lld", &x);
        int l = 0, r = last, aans = 0;
        while(l <= r) {
            int mid = (l+r) >> 1;
            if(ans[mid] <= x) r = mid-1, aans = mid;
            else l = mid+1;
        }
        printf("%d\n", aans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值