2019牛客暑期多校训练营(第六场)

Contest Info


[Practice Link](https://ac.nowcoder.com/acm/contest/886#question)
Solved A B C D E F G H I J
7/10 O O O O O - Ø - - O
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Garbage Classification

按题意模拟即可。

#include <bits/stdc++.h>
using namespace std;
 
#define N 2010
char s[N], t[N], mp[N];
 
int main() {
    int T; scanf("%d", &T);
    for (int kase = 1; kase <= T; ++kase) {
        printf("Case #%d: ", kase);
        scanf("%s%s", s + 1, t);
        for (int i = 0; i < 26; ++i) {
            mp['a' + i] = t[i];
        }
        int d = 0, w = 0, h = 0;
        int len = strlen(s + 1);
        for (int i = 1; i <= len; ++i) {
            int c = mp[s[i]];
            if (c == 'w') ++w;
            else if (c == 'd') ++d;
            else ++h;
        }
        if (h * 4 >= len) {
            puts("Harmful");
        } else if (h * 10 <= len) {
            puts("Recyclable");
        } else if (d >= w * 2) {
            puts("Dry");
        } else {
            puts("Wet");
        }
    }
    return 0;
}

B. Shorten IPv6 Address

题意:
给出\(Ipv6\)地址的缩写规则,要求将\(128\)位的地址缩写成长度最小,多解输出字典序最小的解。

思路:

  • 先去掉所有前导\(0\)
  • 然后枚举一段连续的\(0\),变成\('::'\),然后枚举所有可行解,取最优解即可

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 1010
char s[N];
int a[8];
vector <string> res;
 
string f(int x) {
    string res = "";
    if (!x) return "0";
    while (x) {
        int y = x % 16;
        if (y < 10) res += y + '0';
        else if (y == 10) res += 'a';
        else if (y == 11) res += 'b';
        else if (y == 12) res += 'c';
        else if (y == 13) res += 'd';
        else if (y == 14) res += 'e';
        else res += 'f';
        x /= 16;
    }  
    reverse(res.begin(), res.end());
    return res;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int T; cin >> T;
    for (int kase = 1; kase <= T; ++kase) {
        cout << "Case #" << kase << ": ";
        cin >> (s + 1);
        int len = 128;
        for (int i = 1; i <= len; i += 16) {
            int num = 0;
            for (int j = i; j < i + 16; ++j) {
                num = num * 2 + s[j] - '0';
            }
    //      cout << num << endl;
            a[i / 16] = num;
        }      
        res.clear();
        string str = f(a[0]);
        for (int i = 1; i < 8; ++i) {
            str += ":";
            str += f(a[i]);
        }
        res.push_back(str);
        string tmp = "";
        for (int i = 0; i < 8; ++i) {
            string now = tmp;
            now += ":";
            int j = i;
            for (; j < 8; ++j) {
                if (a[j])
                    break;
            }
            for (int k = j; k < 8; ++k) {
                now += ":";
                now += f(a[k]);
            }
            if (j >= 8) now += ":";
            if (j > i + 1) res.push_back(now);
            if (i) tmp += ":";
            tmp += f(a[i]);
        }
        sort(res.begin(), res.end(), [](string x, string y){
            if (x.length() != y.length())
                return x.length() < y.length();
            return x < y;       
        });
    //  cout << "####\n";
    //  for (auto it : res) cout << it << endl;
        cout << res[0] << "\n";
    }
    return 0;
}

C. Palindrome Mouse

题意:
给出一个字符串,将所有回文子串加入一个集合,然后询问集合中存在多少对\((a, b)\)使得\(a\)\(b\)的子串

思路:
考虑本质不同的回文子串个数只有\(O(n)\)个,那么枚举每个本质不同的回文子串的末端点\(r\),根据其长度可以找到左端点\(l\)
那么这个子串的贡献就是\([l, r]\)区间本质不同的回文子串个数\(-1\)
那么就变成了这个题我做过.jpg

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
#define ALP 26
struct PAM{        
    int Next[N][ALP];
    int fail[N];     
    int cnt[N];      
    int sum[N];       
    int len[N];      
    int s[N];        
    int last;      
    int n;           
    int p;       
    int d[N], up[N];
    int pos[N], sze[N];
    vector<int> G[N];
    int newnode(int w){
        for(int i=0;i<ALP;i++)
            Next[p][i] = 0;
        cnt[p] = 0;
        len[p] = w;
        return p++;
    }
    void init(){
        p = 0;
        newnode(0);
        newnode(-1);
        last = 0;
        n = 0;
        s[n] = -1;      
        fail[0] = 1;
    }
    int get_fail(int x){
        while(s[n-len[x]-1] != s[n]) x = fail[x];
        return x;
    }
    bool add(int c){
        bool F = 0;
        c -= 'a';
        s[++n] = c;
        int cur = get_fail(last);
        if(!Next[cur][c]){
            int now = newnode(len[cur]+2);
            fail[now] = Next[get_fail(fail[cur])][c];
            Next[cur][c] = now;
            d[now] = len[now] - len[fail[now]];
            up[now] = (d[fail[now]] == d[now] ? up[fail[now]] : now); 
            F = 1;
        }
        last = Next[cur][c];
        cnt[last]++;
        return F;
    }
    void build() {
        pos[0] = 0;
        for (int i = 0; i <= p; ++i) G[i].clear();
        for (int i = 0; i <= p; ++i) {
            if (i != 1) {
                G[fail[i]].push_back(i);
            }
        }
    }
    void DFS(int x) {
        pos[x] = ++pos[0], sze[x] = 1;
        for (auto y : G[x]) {
            DFS(y);
            sze[x] += sze[y];   
        }
    }
}pam;
 
struct BIT {
    ll a[N];
    void init() {
        memset(a, 0, sizeof a);
    }
    void update(int x, ll v) {
        for (; x < N; x += x & -x) {
            a[x] += v;
        }
    }
    ll query(int x) {
        ll res = 0;
        for (; x > 0; x -= x & -x) {
            res += a[x];
        }
        return res;
    }
}bit;
 
struct SEG {
    int t[N << 2];
    void init() {
        memset(t, 0, sizeof t);
    }
    void update(int id, int l, int r, int x, int v) {
        if (l == r) {
            t[id] = max(t[id], v);
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid) update(id << 1, l, mid, x, v);
        else update(id << 1 | 1, mid + 1, r, x, v);
        t[id] = max(t[id << 1], t[id << 1 | 1]);
    }
    int query(int id, int l, int r, int ql, int qr) {
        if (l >= ql && r <= qr) {
            return t[id];
        }
        int mid = (l + r) >> 1;
        int res = 0;
        if (ql <= mid) res = max(res, query(id << 1, l, mid, ql, qr));
        if (qr > mid) res = max(res, query(id << 1 | 1, mid + 1, r, ql, qr));
        return res;
    }
}seg;
 
int n, p[N];
char s[N];
 
int main() {
    int T; scanf("%d", &T);
    for (int kase = 1; kase <= T; ++kase) {
        printf("Case #%d: ", kase);
        scanf("%s", s + 1);
        n = strlen(s + 1);
        pam.init();
        for (int i = 1; i <= n; ++i) {
            if (pam.add(s[i])) {
                p[i] = pam.p - 1;
            } else {
                p[i] = -1;
            }
        }
        pam.build(); pam.DFS(1);
        ll res = -pam.p + 2;
        bit.init(); seg.init();
        int now = 1;
        for (int i = 1; i <= n; ++i) {
            while (s[i] != s[i - pam.len[now] - 1]) now = pam.fail[now];
            now = pam.Next[now][s[i] - 'a'];
            for (int x = now; x; x = pam.fail[pam.up[x]]) {
                int l = max(1, seg.query(1, 1, pam.pos[0], pam.pos[x], pam.pos[x] + pam.sze[x] - 1) - pam.len[x] + 2);
                int r = i - pam.len[pam.up[x]] + 2;
                bit.update(l, 1);
                bit.update(r, -1);
            }
            seg.update(1, 1, pam.pos[0], pam.pos[now], i);
            if (p[i] != -1) {
                int l = i - pam.len[p[i]] + 1;
                res += bit.query(l);
            }
        }
        printf("%lld\n", res);
    }
    return 0;
}

D. Move

题意:
\(k\)个箱子,体积一样,有\(n\)件物品,体积分别为\(v_i\)
要求按照以下策略装箱:

  • 先尽可能装满当前箱子,才考虑下一个箱子
  • 对于每个箱子,每次选择剩下的最大的能装的装进去,直到没有一个物品能装进去
    问箱子们的体积至少需要多少?

思路:
首先箱子体积跟答案没有单调性,如:

15 5
39 39 39 39 39 60 60 60 60 60 100 100 100 100 100
199 为一个合法的答案,但 200 不是,201 也不是。

那么咱们就考虑枚举,我们当时考虑的是递增的时候增量不是\(1\),而是\(\text{用剩余的未装物品的体积 - 箱子剩余体积} / k\)
但实际上这样做只是会很快到达下界\(\left\lceil \frac{sum}{k} \right\rceil\),并且上界是\(\left\lceil \frac{sum}{k} \right\rceil + maxV\)

  • 假设某个界不能装下所有物品,那么每个箱子的剩余空间都$ < maxV$
  • 那么有\(k \cdot (ans - maxV + 1) \leq sum\)
  • \(ans \leq \frac{sum}{k} + maxV - 1\)
    所以直接枚举即可,时间复杂度\(\mathcal{O}(maxV \cdot nlogn)\)

代码:

#include <bits/stdc++.h>
using namespace std;

#define N 1010
#define INF 0x3f3f3f3f
int n, k, a[N];

int check(int x) {
	int sum = 0;
	int box = 0;
	multiset <int> se;
	for (int i = 1; i <= n; ++i) {
		se.insert(a[i]);
		sum += a[i];
	}
	for (int i = 1; i <= k; ++i) {
		int remind = x;
		while (!se.empty()) {
			auto pos = se.upper_bound(remind);
			if (pos != se.begin()) {
				--pos;
				remind -= (*pos);
				sum -= (*pos);
				se.erase(pos);
			} else {
				break;
			}
		}
		box += remind;
		if (se.empty()) return -INF;
	} 
	return sum - box;
}

int main() {
	int T; scanf("%d", &T);
	for (int kase = 1; kase <= T; ++kase) {
		printf("Case #%d: ", kase);
		scanf("%d%d", &n, &k);
		int sum = 0;
		for (int i = 1; i <= n; ++i) scanf("%d", a + i), sum += a[i]; 
		for (int i = sum / k; ; ) {
			int x = check(i);
			if (x == -INF) {
				printf("%d\n", i);
				break;
			} else {
				++i;
			//	i += max(1, x / k + (x % k != 0));
			}
		}
	}
	return 0;
}

E. Androgynos

题意:
给出一个\(n\)个点的图,要求构造一个无向自补图。

思路:

  • 一个图和其补图同构的必要条件是边数相同,即\(n\)阶完全图的边数是偶数。
  • 注意到\(n\)阶无向完全图的边数是\(\frac{n(n - 1)}{2}\),那么\(n = 4k\)或者\(n = 4k + 1\)时成立。
  • \(n = 4k\)
    • \(n = 4\)时,连成一条链
    • \(n = 4k(k > 1)\)时,可以先把顶点分成\(4\)块,\(2\)块内部连成团,\(2\)块内部不连组成独立集,然后按\(n = 4\)的情况连块之间的边
  • \(n = 4k + 1\)
    • 多出来的一个点向所有团连边

代码:

#include <bits/stdc++.h>
 
using namespace std;
 
#define N 2010
 
int n;
int G[N][N];
int ans[N];
 
int main() {
//    freopen("input.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    for (int cas = 1; cas <= T; ++cas) {
        printf("Case #%d: ", cas);
        scanf("%d", &n);
        if (n % 4 == 2 || n % 4 == 3) {
            puts("No");
            continue;
        }
        memset(G, 0, sizeof G);
        puts("Yes");
        if (n % 4 == 0) {
            for (int i = 1; i <= n; i += 4) {
                int x = i, y = i + 1, z = i + 2, w = i + 3;
                ans[x] = y;
                ans[y] = w;
                ans[z] = x;
                ans[w] = z;
                G[x][y] = G[y][x] = 1;
                G[y][z] = G[z][y] = 1;
                G[z][w] = G[w][z] = 1;
                for (int j = 1; j < i; ++j) {
                    G[x][j] = G[j][x] = 1;
                    G[w][j] = G[j][w] = 1;
                }
            }
        } else if (n % 4 == 1) {
            ans[1] = 1;
            for (int i = 2; i <= n; i += 4) {
                int x = i, y = i + 1, z = i + 2, w = i + 3;
                ans[x] = y;
                ans[y] = w;
                ans[z] = x;
                ans[w] = z;
                G[x][y] = G[y][x] = 1;
                G[y][z] = G[z][y] = 1;
                G[z][w] = G[w][z] = 1;
                for (int j = 1; j < i; ++j) {
                    G[x][j] = G[j][x] = 1;
                    G[w][j] = G[j][w] = 1;
                }
            }
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                printf("%d", G[i][j]);
            }
            puts("");
        }
        for (int i = 1; i <= n; ++i) {
            printf("%d%c", ans[i], " \n"[i == n]);
        }
    }
    return 0;
}

G. Is Today Friday?

题意:
给出若干个加密后的日期,问是否存在一种解密序列使得所有日期合法并且都是星期五。

思路:
考虑限制条件非常紧,那么刚开始将全排列加入答案,然后一个一个\(check\),如果不行直接去掉,不用\(check\)两三次,可行解空间就会变得很小。

代码:

#include <bits/stdc++.h>
using namespace std;
 
int n;
vector <string> vec, res;
 
void input(vector <string> &vec) {
    vec.clear();
    string s;
    for (int i = 1; i <= n; ++i) {
        cin >> s;
        vec.push_back(s);
    }
    sort(vec.begin(), vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());
}
 
void get_permutation(vector <string> &res) {
    res.clear();
    string str = "";
    for (int i = 0; i < 10; ++i)
        str += i + '0';
    do {
        res.push_back(str);
    } while (next_permutation(str.begin(), str.end()));
}
int getwee2(int y, int m, int d) {
    int ans;
    if (m == 1 || m == 2) m += 12, y--;
    if ((y < 1752) || (y == 1752 && m < 9) || (y == 1752 && m == 9 && d < 3)) {
        ans = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 + 5) % 7;
    } else {
        ans = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
    }
    ans = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
    return ans + 1;
}
 
int getweek(int y, int m, int d) {
    if (m < 3) {
        y -= 1;
        m += 12;
    }
    int c = y / 100;
    y = y - 100 * c;
    return ((y + y / 4 + c / 4 - 2 * c + (13 * (m + 1)) / 5 + d - 1) % 7 + 7) % 7;
}
 
bool isLeap(int y) {
    return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0);
}
 
int mon[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
 
bool ok(string s, string mp) {
    int y = (mp[s[0] - 'A'] - '0') * 1000 + (mp[s[1] - 'A'] - '0') * 100 + (mp[s[2] - 'A'] - '0') * 10 + (mp[s[3] - 'A'] - '0');
    int m = (mp[s[5] - 'A'] - '0') * 10 + (mp[s[6] - 'A'] - '0');
    int d = (mp[s[8] - 'A'] - '0') * 10 + (mp[s[9] - 'A'] - '0');
    if (y < 1600) return 0;
    if (m <= 0 || m > 12) return 0;
    if (d <= 0 || d > mon[isLeap(y)][m]) return 0;
    if (getweek(y, m, d) != 5) return 0;
    return 1;
}
 
void check(string s, vector <string> &vec) {
    vector <string> tmp;
    for (auto it : vec) {
        if (ok(s, it)) {
            tmp.push_back(it);
        }  
    }
    vec = tmp;
}
 
void solve() {
    for (auto it : vec) {
        check(it, res);
        if (res.empty()) {
            cout << "Impossible\n";
            return;
        }
    }
    sort(res.begin(), res.end());
    cout << res[0] << "\n";
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0); 
    int T; cin >> T;
    for (int kase = 1; kase <= T; ++kase) {
        cout << "Case #" << kase << ": ";
        cin >> n;
        input(vec);
        get_permutation(res);
        solve();
    }
    return 0;
}

J. Upgrading Technology

题意:
\(n\)个武器,第\(i\)个武器从\(j - 1\)升级到\(j\)级需要代价\(c_{i, j}\),如果所有武器都大于等于\(k\)级,那么会获得\(d_k\)的收益。
问如果升级武器使得收益最高。

思路:

  • 如果要获得\(d_k\)的收益,那么需要保证至少有一件武器等级为\(k\),其他武器的等级大于等于\(k\)
  • 不要忘记\(0\)级的收益
    然后前缀搞一搞即可。

代码:

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
#define N 1010
#define INFLL 0x3f3f3f3f3f3f3f3f
 
int n, m;
ll a[N][N], b[N];
ll f[N][N], g[N], f2[N][N];
 
 
int main() {
//    freopen("input.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    for (int cas = 1; cas <= T; ++cas) {
        printf("Case #%d: ", cas);
        scanf("%d %d", &n, &m);
        memset(f, 0, sizeof f);
        memset(f2, 0, sizeof f2);
        memset(g, 0, sizeof g);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                scanf("%lld", &a[i][j]);
            }
        }
        for (int i = 1; i <= m; ++i) {
            scanf("%lld", b + i);
            g[i] = g[i - 1] + b[i];
        }
        ll ans = 0;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                f[i][j] = f[i][j - 1] - a[i][j];
                f2[i][j] = f2[i][j - 1] - a[i][j];
            }
            for (int j = m; j >= 0; --j) {
                if (j + 1 <= m) {
                    f[i][j] = max(f[i][j], f[i][j + 1]);
                }
            }
        }
        for (int j = m; j >= 0; --j) {
            ll tmp = 0;
            for (int i = 1; i <= n; ++i) {
                tmp += f[i][j];
            }
            for (int i = 1; i <= n; ++i) {
                ans = max(ans, tmp - f[i][j] + f2[i][j] + g[j]);
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2019-08-04 08:06  Dup4  阅读(228)  评论(2编辑  收藏  举报