2019牛客多校第六场 7/10

队友写了题,好评如潮

A

来自zkp同学

#include<iostream>
#include<map>
using namespace std;
int main()
{
    int n;
    string a,b;
    map<char,char>mp;
    cin>>n;
    for(int k=1;k<=n;k++)
    {
        cin>>a>>b;
        for(int i=0;i<26;i++)mp[i+'a']=b[i];
        int z,x,c,v;
        z=x=c=v=0;
        for(int i=0;i<a.size();i++)
        {
            char f=mp[a[i]];
            if(f=='d')z++,v++;
            if(f=='w')x++,v++;
            if(f=='h')c++,v++;
        }
        cout<<"Case #"<<k<<": ";
        if(c*4>=v)cout<<"Harmful\n";
        else if(c*10<=v)cout<<"Recyclable\n";
        else if(z>=x*2)cout<<"Dry\n";
        else cout<<"Wet\n";
    }
}

B

来自小洛洛,每次直接替换掉一整段0,然后string暴力比较一下大小即可

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>
#include <array>
#include <cstdint>
#include <sstream>
 
std::string fmt_v6 (const std::string &bin) {
    auto dig = std::array<uint16_t, 8> {};
    for (int i = 0; i < 128; ++i)
        dig[i / 16] = dig[i / 16] * 2 + (bin[i] - '0');
 
    auto mxlen = std::array<int, 8> {};
    int mx = 0;
    for (int i = 0; i < 8; ++i) {
        if (dig[i] == 0)
            mxlen[i] = 1 + (i == 0 ? 0 : mxlen[i - 1]);
        else
            mxlen[i] = 0;
        mx = std::max(mx, mxlen[i]);
    }
 
    if (mx == 8)
        return "::";
 
    auto gen = [&](int l, int r) {
        auto ss = std::ostringstream();
        for (int i = 0; i < 8; ++i)
            if (i == l) {
                i = r - 1;
                ss << "::";
            } else {
                if (i != 0 && i != r)
                    ss << ":";
                ss << std::hex << dig[i];
            }
        return ss.str();
    };
 
    if (mx <= 1)
        return gen(-1, -1);
 
    std::string ret;
    for (int i = 0; i < 8; ++i)
        if (mxlen[i] == mx) {
            auto cur = gen(i - mxlen[i] + 1, i + 1);
            if (ret.length() == 0
                || cur.length() < ret.length()
                || (cur.length() == ret.length() && cur < ret))
                ret = cur;
        }
    return ret;
}
 
int main () {
    std::ios::sync_with_stdio(false);
 
    int n;
    std::cin >> n;
    for (int i = 1; i <= n; ++i) {
        std::string s;
        std::cin >> s;
        auto ans = fmt_v6(s);
        std::cout << "Case #" << i << ": " << ans << "\n";
    }
 
    return 0;
}

C

这题没有意识到所有的回文子串其实就是所有的祖先们的fail,举个栗子,就是bcaabaacb,祖先有caabaac,aabaa,aba,b,这些里面所有的fail加在一起就是这个回文子串的所有回文子串。
那么我们只要dfs的时候,每次暴力往上打标记,因为每次增加2个字母,最多增加两个回文子串,也就是每次打标记最多增加两个回文子串,那么复杂度就是 O ( n ) O(n) O(n)了。
考虑在父亲处累加,也就是所有的子树大小乘上自己打的标记数量(每个标记对于子树里的都有贡献)。

/* ***********************************************
Author        :BPM136
Created Time  :2019/8/4 11:14:49
File Name     :C.cpp
************************************************ */

#include <bits/stdc++.h>
#include <sys/timeb.h>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
#define filein(x) freopen(#x".in","r",stdin)
#define fileout(x) freopen(#x".out","w",stdout)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#define mkd(x) freopen(#x".in","w",stdout);

using namespace std;

int random(int l, int r) {
	static std::random_device rd;
	struct timeb timeSeed;
	ftime(&timeSeed);
	size_t seed = timeSeed.time * 1000 + timeSeed.millitm;  // milli time
	static std::mt19937 gen(seed);
	std::uniform_int_distribution<> u(l, r);
	return u(gen);
}

typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef pair<int, int> pii;

struct PalindromicTree {
	static int const N = 200005;

	int nxt[N][26];
	int fail[N];
	int len[N]; //该节点表示的回文子串的长度
	int s[N], L, R;   /表示要插入的字符串
	int cont[N]; /表示这个节点对于回文子串总数的贡献,如果在线询问总数就用这个算就好了,因为要加1的节点一定是这个节点跳fail到根路径上的所有节点,那么也就是cont[fail] + 1
	int cnt[N];   //表示这个节点表示的回文串的个数,最后需要count函数计数才行
	int last[2]; 0表示左边插入的last,1表示右边插入的last
	int p;      ///节点数目    p - 2 就是本质不同的回文串个数(除掉表示偶数长度和奇数长度的树根)
	ll sum;     ///整个串回文子串的个数(包括不同位置出现的同样的回文子串)
	int sz[N], c[N];
	int vis[N];

	int newNode(int _len) {
		for(int i = 0; i < 26; ++i)
			nxt[p][i] = 0;
		cont[p] = 0;
		len[p] = _len;
		return p++;
	}

	void init(int n) {    ///n表示这个字符串的最长长度
		p = 0;
		newNode(0);
		newNode(-1);

		std::fill(std::begin(s), std::end(s), -1);
		std::fill(std::begin(cnt), std::end(cnt), 0);
		L = n;
		R = n - 1;
		last[0] = last[1] = 1;
		fail[0] = 1;
		sum = 0;
	}

	int getFail(int v, bool ty) {
		int bord = ty == 1 ? R : L;
		int cons = ty == 1 ? -1 : 1;
		while(s[bord + cons * (len[v] + 1)] != s[bord]) 
			v = fail[v];
		return v ;
	}

	void add(int c, bool ty) {
		if(ty) 
			s[++R] = c;
		else 
			s[--L] = c;
		int cur = getFail(last[ty], ty);
		if(nxt[cur][c] == 0) {
			int now = newNode(len[cur] + 2);
			fail[now] = nxt[getFail(fail[cur], ty)][c];
			nxt[cur][c] = now;
			cont[now] = cont[fail[now]] + 1;
			///要维护新的东西就从cur转移来就好了
		}
		last[ty] = nxt[cur][c];
		if(len[last[ty]] == R - L + 1) 
			last[ty ^ 1] = last[ty];
		sum += cont[last[ty]];
		++cnt[last[ty]];
	}

	void count() {
		for(int i = p - 1; i > 1; --i) 0和1为偶数长度和奇数长度的根节点,所以不能计数
			cnt[fail[i]] += cnt[i];
	}

	int dfs(int x) {
		sz[x] = 1;
		c[x] = 0;
		for (int y = x; y >= 2 && vis[y] == 0; y = fail[y]) {
			++c[x];
			vis[y] = x;
		}
		for (int i = 0; i < 26; ++i)
			if (nxt[x][i]) 
				sz[x] += dfs(nxt[x][i]);
		for (int y = x; y >= 2 && vis[y] == x; y = fail[y]) 
			vis[y] = 0;
		return sz[x];
	}

	ll solve() {
		ll ret = 0;
		dfs(0);
		dfs(1);
		for (int i = 2; i < p; ++i) 
			ret += 1LL * sz[i] * c[i];
		return ret - p + 2;
	}
}tree; 

int main() {
	USE_CIN_COUT;
	int T;
	cin >> T;
	string s;
	for (int o = 1; o <= T; ++o) {
		cin >> s;
		tree.init(SZ(s));
		for (auto c : s)
			tree.add(c - 'a', 0);
		ll ans = tree.solve();
		cout << "Case #" << o << ": " << ans << '\n';
	}
    return 0;
}

D

二分答案就行,不过要注意到答案并不单调,但是每次的不单调区间不超过maxV,而且并不大,那么暴力调参就调过去了x
其实枚举一下上下界也是一样的

#include <iostream>
#include <numeric>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>
#include <array>
#include <cstdint>
#include <set>
 
int check (std::vector<int> const &v, int cap) {
    auto s = std::multiset<int>(std::begin(v), std::end(v));
 
    int cnt = 0;
    while (!s.empty()) {
        int rest = cap;
        ++cnt;
 
        for (;;) {
            auto it = s.upper_bound(rest);
            if (it == s.begin())
                break;
            --it;
            rest -= *it;
            s.erase(it);
        }
    }
 
    return cnt;
}
 
// (ERROR * log2(sum{v} / WINDOW) + WINDOW) * n log n
int solve (std::vector<int> const &v, int k) {
    int l = *std::max_element(std::begin(v), std::end(v));
    int r = std::accumulate(std::begin(v), std::end(v), 0);
    if (k == 1)
        return r;
    if (check(v, l) <= k)
        return l;
 
    const int WINDOW = 100;
    const int ERROR = 30;
 
    while (r - l > WINDOW) {
        int m = l + (r - l) / 2;
 
        int m1 = m - ERROR, m2 = m + ERROR;
        bool ok = false;
        for (int i = m1; i < m2; ++i)
            if (check(v, i) <= k) { // Already ok.
                ok = true;
                r = i;
                break;
            }
 
        if (!ok)
            l = m2;
    }
 
    while (l < r && check(v, l) > k) // until <= k
        ++l;
    return l;
}
 
int main () {
    std::ios::sync_with_stdio(false);
 
    // int n;
    // std::cin >> n;
    // auto v = std::vector<int>(n);
    // for (auto &c: v)
    //     std::cin >> c;
 
    // int mx = *std::max_element(std::begin(v), std::end(v));
    // for (int i = mx; i <= mx + 30; ++i)
    //     std::cout << i << "\t" << check(v, i) << "\n";
 
    int t;
    std::cin >> t;
    for (int i = 1; i <= t; ++i) {
        int n, k;
        std::cin >> n >> k;
        auto v = std::vector<int>(n);
        for (auto &c: v)
            std::cin >> c;
 
        auto ans = solve(v, k);
        std::cout << "Case #" << i << ": " << ans << "\n";
    }
 
    return 0;
}

E

提供两个代码,一个是我和zkp写的,一个是小洛洛的
首先自补图存在的充要条件是n为4k或者4k+1,只要发现自己和补图加在一起是一个完全图,而且边数相同,那么这个完全图的边数一定是偶数。充分性的证明用度数序列构造一下就行。
那么就是考虑构造了,如果是4k,由可图性的定理,首先分成2份,每份2k个点。
然后度数序列 3 k − 1 , 3 k − 1 , 3 k − 1 , . . . , k , k , k , . . . 3k-1,3k-1,3k-1,...,k,k,k,... 3k1,3k1,3k1,...,k,k,k,...一定是可图的。
这个度数序列对应的图的样子就是,首先把前面的2k个点连成完全图,然后后面的2k个点围成一圈,往对应的连续的k个点连起来。
这是4k的时候的样子
如果是4k+1,那么多建一个度数为一半的点就行
最后是我写了4k的部分,zkp写了4k+1的部分
后来小洛洛补了一个不断加点的代码
这是小洛洛的代码

#include <iostream>
#include <numeric>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>
#include <array>
#include <cstdint>
#include <set>
 
using Mat = std::vector<std::vector<char>>;
using Ans = std::pair<Mat, std::vector<int>>;
 
Ans gen_ans (int n) {
    return { Mat(n, std::vector<char>(n, 0)), std::vector<int>(n) };
}
 
Ans solve (int n) {
    auto ans = gen_ans(n);
    auto set = [&](int i, int j) {
        ans.first[i][j] = ans.first[j][i] = 1;
    };
 
    for (int i = 0; i < n / 2; ++i)
        for (int j = i + 1; j < n / 2; ++j)
            set(i, j);
    for (int i = 0; i < n / 2; ++i)
        for (int j = 0; j < n / 4; ++j)
            set(n / 2 + i, (i + j) % (n / 2));
 
    for (int i = 0; i < n / 2; ++i)
        // ans.second[i] = n / 2 + i;
        ans.second[n / 2 + i] = i;
    for (int i = 0; i < n / 2; ++i)
        // ans.second[n / 2 + i] = (-1 + i + n / 2) % (n / 2);
        ans.second[(-1 + i + n / 2) % (n / 2)] = n / 2 + i;
 
    if (n & 1) {
        for (int i = 0; i < n / 2; ++i)
            set(n - 1, i);
        ans.second[n - 1] = n - 1;
    }
 
    return ans;
}
 
int main () {
    std::ios::sync_with_stdio(false);
 
    int t;
    std::cin >> t;
    for (int i = 1; i <= t; ++i) {
        int n;
        std::cin >> n;
        std::cout << "Case #" << i << ": ";
 
        if (n % 4 >= 2) {
            std::cout << "No\n";
            continue;
        }
 
        Ans ans = solve(n);
 
        std::cout << "Yes\n";
        for (int i = 0; i < n; ++i, std::cout << '\n')
            for (int j = 0; j < n; ++j)
                std::cout << (int)ans.first[i][j];
        for (int i = 0; i < n; ++i) {
            std::cout << ans.second[i] + 1;
            std::cout << (i == n - 1 ? '\n' : ' ');
        }
    }
 
    return 0;
}

这是我和zkp的组合代码

/* ***********************************************
Author        :BPM136
Created Time  :2019/8/3 16:26:39
File Name     :E.cpp
************************************************ */
 
#include <bits/stdc++.h>
#include <sys/timeb.h>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
#define filein(x) freopen(#x".in","r",stdin)
#define fileout(x) freopen(#x".out","w",stdout)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#define mkd(x) freopen(#x".in","w",stdout);
 
using namespace std;
 
int random(int l, int r) {
    static std::random_device rd;
    struct timeb timeSeed;
    ftime(&timeSeed);
    size_t seed = timeSeed.time * 1000 + timeSeed.millitm;  // milli time
    static std::mt19937 gen(seed);
    std::uniform_int_distribution<> u(l, r);
    return u(gen);
}
 
typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef pair<int, int> pii;
 
void solve4n(int sum) {
    static bool w[2001][2001];
    static int f[2001];
    for (int i = 1; i <= sum; ++i)
        for (int j = 1; j <= sum; ++j)
            w[i][j] = 0;
    int n = sum / 4;
    memset(w, 0, sizeof(w));
    for (int i = 1; i <= n * 2; ++i)
        for (int j = 1; j <= n * 2; ++j)
            w[i][j] = i != j;
    for (int i = 1; i <= n * 2; ++i) {
        int now = 2 * n + i, p = i;
        for (int j = 1; j <= n; ++j) {
            w[now][p] = w[p][now] = 1;
            ++p;
            if (p > 2 * n)
                p -= 2 * n;
        }
    }
    for (int i = 1; i <= sum; ++i) {
        for (int j = 1; j <= sum; ++j)
            cout << w[i][j];
        cout << '\n';
    }
    for (int i = 1; i <= n; ++i)
        f[i] = 2 * n + i;
    for (int i = 1; i <= n; ++i)
        f[i + n] = 3 * n + i;
    int p = n * 2;
    for (int i = 1; i <= n; ++i) {
        f[i + n * 2] = p;
        ++p;
        if (p >= 2 * n)
            p -= 2 * n;
    }
    for (int i = 1; i <= n; ++i) {
        f[i + n * 3] = p;
        ++p;
        if (p >= 2 * n)
            p -= n * 2;
    }
    cout << f[1];
    for (int i = 2; i <= sum; ++i)
        cout << ' ' << f[i];
    cout << '\n';
}
 
void solve4n_1(int n) {
    static bool map[2001][2001];
    static int f[2001];
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)map[i][j]=0;
    int r = n / 2;
    for(int i=0;i<r;i++)for(int j=i+1;j<r;j++)map[i][j]=map[j][i]=1;
    for(int i=0;i<r;i++)map[i][r]=map[r][i]=1;
    for(int i=0;i<r;i++)for(int j=0;j<r/2;j++)
    {
        map[i][(j+i)%r+r+1]=map[(j+i)%r+r+1][i]=1;
    }
    for(int i=1;i<=r;i++)f[i]=i+r+1;
    f[r+1]=r+1;
    for(int i=1;i<=r;i++)f[i+r+1]=(i)%r+1;
    for(int i=0;i<n;i++) {
        for(int j=0;j<n;j++)
            cout<<map[i][j];
        cout << '\n';
    }
    cout << f[1];
    for(int i=2;i<=n;i++)
        cout<<' '<<f[i];
    cout << '\n';
}
 
int main() {
    USE_CIN_COUT;
    int T;
    cin >> T;
    for (int o = 1; o <= T; ++o) {
        int n;
        cin >> n;
        cout << "Case #" << o << ": ";
        if (n % 4 == 0) {
            cout << "Yes" << '\n';
            solve4n(n);
            continue;
        }
        if (n % 4 == 1) {
            cout << "Yes" << '\n';
            solve4n_1(n);
            continue;
        }
        cout << "No" << '\n';
    }
    return 0;
}

F

G

了解了蔡勒公式和基姆拉尔森计算公式。就是给你一个日期,算这是星期几,不得不说后者好用的多。
然后接下来爆搜就可以了,不过要注意去重和2月29日.

/* ***********************************************
Author        :BPM136
Created Time  :2019/8/4 17:59:43
File Name     :G.cpp
************************************************ */

#include <bits/stdc++.h>
#include <sys/timeb.h>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
#define filein(x) freopen(#x".in","r",stdin)
#define fileout(x) freopen(#x".out","w",stdout)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#define mkd(x) freopen(#x".in","w",stdout);

using namespace std;

int random(int l, int r) {
	static std::random_device rd;
	struct timeb timeSeed;
	ftime(&timeSeed);
	size_t seed = timeSeed.time * 1000 + timeSeed.millitm;  // milli time
	static std::mt19937 gen(seed);
	std::uniform_int_distribution<> u(l, r);
	return u(gen);
}

typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int sum_d[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

int getNum(string s, vector<int> const& f) {
	int ret = 0;
	for (auto const& c : s)
		ret = ret * 10 + f[c - 'A'];
	return ret;
}

bool chk(int y, int m, int d) {
	if (y < 1600 || m <= 0 || m > 12 || d <= 0)
		return 0;
	int lim = sum_d[m];
	if (m == 2 && ((y % 4 == 0 && y % 100) || y % 400 == 0))
		++lim;
	if (d > lim)
		return 0;
	if (m == 1 || m == 2) 
		m += 12, --y;
	auto w = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;//此为其他公式,直接输出w即可
	return (w + 7) % 7 == 5;
}

int main() {
	USE_CIN_COUT;
	int T;
	cin >> T;
	for (int o = 1; o <= T; ++o) {
		int n;
		cin >> n;
		auto f = vector<int>(10);
		for (int i = 0; i < 10; ++i)
			f[i] = i;
		auto a = vector<string>(n);
		for (auto& v : a)
			cin >> v;
		sort(all(a));
		a.erase(unique(all(a)), a.end());

		cout << "Case #" << o << ": ";
		bool ans = 0;
		do {
			bool can = 1;
			for (auto const& v : a) {
				int yy = getNum(v.substr(0, 4), f);
				int mm = getNum(v.substr(5, 2), f);
				int dd = getNum(v.substr(8, 2), f);
				if (chk(yy, mm, dd) == 0) {
					can = 0;
					break;
				}
			}
			if (can) {
				for (auto v : f)
					cout << v;
				cout << '\n';
				ans = 1;
				break;
			}
		} while (next_permutation(all(f)));
		if (ans == 0) 
			cout << "Impossible" << '\n';
	}
    return 0;
}

H

I

J

贪心即可。
每次枚举最低的等级,维护最大的前缀和,如果有物品的最大的前缀和就是当前的等级,那么就直接更新ans,否则枚举一下是谁被一定要求是当前等级即可

/* ***********************************************
Author        :BPM136
Created Time  :2019/8/3 12:51:28
File Name     :J.cpp
************************************************ */
 
#include <bits/stdc++.h>
#include <sys/timeb.h>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
#define filein(x) freopen(#x".in","r",stdin)
#define fileout(x) freopen(#x".out","w",stdout)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#define mkd(x) freopen(#x".in","w",stdout);
 
using namespace std;
 
int random(int l, int r) {
    static std::random_device rd;
    struct timeb timeSeed;
    ftime(&timeSeed);
    size_t seed = timeSeed.time * 1000 + timeSeed.millitm;  // milli time
    static std::mt19937 gen(seed);
    std::uniform_int_distribution<> u(l, r);
    return u(gen);
}
 
typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
 
int const N = 1005;
 
pli mx[N];
int c[N][N], d[N];
ll sum[N][N];
ll sum_d[N];
int n, m;
 
void Remax(pli& t, ll val, int pos) {
    if (val == t.first)
        t.second = pos;
    if (val > t.first)
        t = make_pair(val, pos);
}
 
int main() {
    USE_CIN_COUT;
    int T;
    cin >> T;
    for (int o = 1; o <= T; ++o) {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                cin >> c[i][j];
                c[i][j] = -c[i][j];
                sum[i][j] = sum[i][j - 1] + c[i][j];
            }
        for (int j = 1; j <= m; ++j) {
            cin >> d[j];
            sum_d[j] = sum_d[j - 1] + d[j];
        }
        for (int i = 1; i <= n; ++i)
            mx[i] = make_pair(-1e9 * 1000, -1);
        ll ans = 0;
        for (int i = m; i >= 0; --i) {
            ll anss = sum_d[i], sumc = 0;
            bool flag = 0;
            for (int j = 1; j <= n; ++j) {
                Remax(mx[j], sum[j][i], i);
                sumc += mx[j].first - sum[j][i];
                anss += sum[j][i];
                if (mx[j].second == i)
                    flag = 1;
            }
            assert(sumc >= 0);
            if (flag)
                ans = max(ans, anss + sumc);
            else {
                for (int j = 1; j <= n; ++j)
                    ans = max(ans, anss + sumc - mx[j].first + sum[j][i]);
            }
        }
        cout << "Case #" << o << ": " << ans << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值