2019 牛客 多校赛 第六场

slove  4/10

rank  211

补题   7/10

--------------------------------------------------------

https://ac.nowcoder.com/acm/contest/886#question

A、Garbage Classification

差6秒一血,血亏。。。

#include 
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
char s1[10005], s2[10005];
int main()
{
    int T, cas = 1;
    sc("%d", &T);
    while (T--)
    {
        sc("%s%s", s1, s2);
        int q = 0, w = 0, e = 0;
        int len = strlen(s1);
        for (int i = 0; i < len; i++)
        {
            if (s2[s1[i] - 'a'] == 'h')
                q++;
            else if (s2[s1[i] - 'a'] == 'd')
                w++;
            else
                e++;
        }
        printf("Case #%d: ", cas++);
        if (q * 4 >= len)
            printf("Harmful\n");
        else if (q * 10 <= len)
            printf("Recyclable\n");
        else if (w >= 2 * e)
            printf("Dry\n");
        else
            printf("Wet\n");
    }
}

B、Shorten IPv6 Address

题意:    给你一个128位的字符串.求缩短后的字符串.缩短的规则如下:每四位缩成一个字符,然后每四个字符打引号.输出的时候要去掉每一块中的前导0,并且如果有两个以上连续的块为0的话可以缩短为"::",但是只能缩一次,求长度最短的情况下字典序最小的字符串
题解:    模拟..可以直接枚举删掉那一段0,然后排序,输出最小的 

#include
using namespace std;
int a[300],cnt=0,b[300],tot;
char s[300];
char biao[30]={"0123456789abcdef"};
vector> list1;
vector list2;
bool cmp(string a,string b)
{
    if(a.size()!=b.size())
        return a.size()r){
            if(b[i]==0){
                temp+="0";
//              printf("0");
            }
            else{
                if(a[i*4]==0&&a[i*4+1]==0&&a[i*4+2]==0){
                    temp+=biao[a[i*4+3]];
//                  printf("%x",a[i*4+3]);
                }
                else if(a[i*4]==0&&a[i*4+1]==0){
                    temp=temp+biao[a[i*4+2]]+biao[a[i*4+3]];
//                  printf("%x%x",a[i*4+2],a[i*4+3]);
                }
                else if(a[i*4]==0){
                    temp=temp+biao[a[i*4+1]]+biao[a[i*4+2]]\
                    +biao[a[i*4+3]];
//                  printf("%x%x%x",a[i*4+1],a[i*4+2],a[i*4+3]);
                }
                else{
                    temp=temp+biao[a[i*4]]+biao[a[i*4+1]]+biao[a[i*4+2]]\
                    +biao[a[i*4+3]];
                }
//                  printf("%x%x%x%x",a[i*4],a[i*4+1],a[i*4+2],a[i*4+3]);
            }
            if(i!=tot-1&&(i+1r)){
                temp+=":";
            }
        }
        else{
            if(!flag){
                temp+="::";
                flag=true;
            }
        }
    }
    list2.push_back(temp);
}
void solve(){
    int t;
    scanf("%d",&t);
    int z=0;
    while(t--){
        ++z;
        list1.clear();
        list2.clear();
        scanf("%s",s);
        cnt=0,tot=0;       
        printf("Case #%d: ",z);
        for(int i=0;s[i];i+=4){
            int num = 0;
            num+=(s[i]-'0')*8+(s[i+1]-'0')*4+(s[i+2]-'0')*2+\
            (s[i+3]-'0')*1;
            a[cnt++]=num;
        }
        for(int i=0;i=0&&b[i-1]!=0){
                    l=r=i;
                }
                else if(i==0){
                    l=r=i;
                }
                else{
                    r=i;
                }
            }
            else{
                if(i-1>=0&&b[i-1]==0){
                    list1.push_back({l,r});
                }
            }
        }
        if(b[tot-1]==0){
            list1.push_back({l,tot-1});
        }
        int maxn = -1,maxl=100000,maxr=-1;
        for(int i=0;i=2){
                calc(list1[i].first,list1[i].second);
            }
        }
        sort(list2.begin(),list2.end(),cmp);
        //for(int i=0;i

C、Palindrome Mouse

 

题意: 求出所有的回文子串,取x子串和y子串 如果x子串是y子串的子串 则称为满足条件一对求满足条件的对数
做法:因为需要求出回文子串,所以考虑回文自动机,又因为需要满足x是y的子串 回文自动机是原理是 在原有回文串的基础加入字符。所以自动机的每个节点通过nex向下搜索的节点数和fail向上搜索的节点数相乘是这个节点的贡献。但是减去1因为要去除本身和本身。

#include
using namespace std;
const int N = 1e5 + 10;
#define ll long long
struct PAM
{
    int s[N], nex[N][26], link[N], len[N], fail[N];
    int num[N], cnt[N], id[N];
    int length, idx, last;
    void newnode(int l)
    {
        len[idx] = l;
        memset(nex[idx], 0, sizeof(nex[idx]));
    }
    void init()
    {
        idx = 1, last = 0;
        len[0] =0, len[1] = -1;
        cnt[1] = cnt[0] = 0;
        num[1] = num[0] = 0;
        memset(nex[0], 0, sizeof(nex[0]));
        memset(nex[1], 0, sizeof(nex[1]));
        length = 0;
        s[length] = -1;
        fail[0] = 1;
    }
    int get_fail(int x)
    {
        while (s[length - len[x] - 1] != s[length])
            x = fail[x];
        return x;
    }
    void insert(int x)
    {
        s[++length] = x;
        int p = get_fail(last);
        if (!nex[p][x])
        {
            ++idx;
            id[idx] = length;/*出现位置*/
            newnode(len[p] + 2);
            fail[idx] = nex[get_fail(fail[p])][x];
            nex[p][x] = idx;
            num[idx] = num[fail[idx]] + 1;
        }
        last = nex[p][x];
        cnt[last] ++;
    }
    void count()
    {
        for (int i = idx; i >= 2; i--)
            cnt[fail[i]] += cnt[i];
    }
}pam;
char str[N];
ll up[N], down[N];
bool vis[N];
int dfs(int x)
{
    up[x] = 0;
    vector v;
    for (int i = x; i >= 2; i = pam.fail[i])
    {
        if (vis[i])
            break;
        v.push_back(i);
        vis[i] = true;
        up[x]++;
    }
    down[x] = 1;
    for (int i = 0; i < 26; i++)
        if (pam.nex[x][i] != 0)
            down[x] += dfs(pam.nex[x][i]);
    for (int i = 0; i < v.size(); i++)
        vis[v[i]] = false;
    return down[x];
}
int main()
{
    int T;
    scanf("%d", &T);
    int Case = 1;
    while (T--)
    {
        scanf("%s", str);
        pam.init();
        int len = strlen(str);
        for (int i = 0; i < len; i++)
            pam.insert(str[i] - 'a');
        dfs(0), dfs(1);
        ll ans = 0;
        for (int i = 2; i <= pam.idx; i++)
            ans += (up[i] * (down[i]));
        ans -= (pam.idx - 1);
        printf("Case #%d: %lld\n",Case++, ans);
    }
}

D、Move

n 个物品,m个背包,背包体积一样,问背包体积至少为多少可以放下所有物品。

对于当前背包,如果有能放入的物品,放入体积最高的物品,直到不能放下物品后,放入下一个背包。

二分,WA了之后发现似乎有一些点不满足单挑性,然后check了一下二分到的答案旁边的值,过了。。。

#include 
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
int num[1005];
vectortt;
int len;
int n, m;
int ge[1005];
bool check(int k)
{
	int cnt = 1;
	int yu = k;
	vectorv;
	for (int i = 0; i < len; i++)
		v.push_back(tt[i]);
	for (int i = 0; i < 1005; i++)
		ge[i] = num[i];
	while (!v.empty())
	{
		auto it = upper_bound(v.begin(), v.end(), yu);
		if (it == v.begin())
		{
			cnt++;
			yu = k;
		}
		else
		{
			it--;
			int val = *it;
			int can = yu / val;
			can = min(can, ge[val]);
			yu -= (val) * can;
			ge[val] -= can;
			if (ge[val] == 0)
				v.erase(it);
		}
	}
	if (cnt <= m)
		return true;
	return false;
}
int main()
{
	int T, cas = 1;
	scanf("%d", &T);
	while (T--)
	{
		tt.clear();
		sc("%d%d", &n, &m);
		int l = 1, r = 1000005, t;
		for (int i = 0; i < 1005; i++)
			num[i] = 0;
		for (int i = 1; i <= n; i++)
		{
			sc("%d", &t);
			num[t]++;
			l = max(l, t);
		}
		int qq = l;
		for (int i = 0; i < 1005; i++)
			if (num[i])
				tt.push_back(i);
		len = tt.size();
		while (l + 1 < r)
		{
			int k = (l + r) / 2;
			if (check(k))
				r = k;
			else
				l = k;
		}
		if (!check(l))
			l = r;
		for (int i = max(qq, l - 1000); i <= l + 200; i++)
		{
			if (check(i))
			{
				l = i;
				break;
			}
		}
		pr("Case #%d: %d\n", cas++, l);
	}
}

E、Androgynos

题意:T组案例,每组案例一个整数 n ,表示有一个顶点数为 n 的图,问你这个图是否和他的补图同构,(也就是自同构)

自同构

1、首先,顶点为 N 的图,一共有 n*(n+1)/2 条边,如果变为奇数,显然不合法,那么只有当 n %4==0||n%4==1 时才会有解。

2、先考虑 n%4==0,考虑将所有点分为4份,将四份中的两份内部联通,再分别与剩下的块中的一块联通,这样就可以自同构了。

2019 牛客 多校赛 第六场_第1张图片                        2019 牛客 多校赛 第六场_第2张图片

3、那么如果 n%4==1,我们只需要将这个点在开始的时候与第一块和第二块联通,这样在补图中,他就与第三第四块联通了,也构成自同构。

Code

#include 
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
int mp[2005][2005];
void add(int x, int y)
{
	mp[x][y] = 1;
	mp[y][x] = 1;
}
int main()
{
	int T, cas = 1;
	sc("%d", &T);
	while (T--)
	{
		int n;
		sc("%d", &n);
		if (n % 4 == 0 || n % 4 == 1)
		{
			for (int i = 1; i <= n; i++)
				for (int j = 1; j <= n; j++)
					mp[i][j] = 0;
			int kuai = n / 4;
			for (int i = 1; i <= kuai; i++)
				for (int j = i + 1; j <= kuai; j++)
				{
					add(i, j);//第一块变成团
					add(kuai + i, kuai + j);//第二块变成团
				}
			for (int i = 1; i <= kuai; i++)
				for (int j = 1; j <= kuai; j++)
				{
					add(i, kuai + j);//一二块联通
					add(kuai + i, kuai * 2 + j);//二三块联通
					add(i, kuai * 3 + j);//一四块联通
				}
			printf("Case #%d: Yes\n", cas++);
			if (n % 4 == 0)
			{
				for (int i = 1; i <= n; i++)
				{
					for (int j = 1; j <= n; j++)
						printf("%d", mp[i][j]);
					printf("\n");
				}
				for (int i = kuai * 2 + 1; i <= kuai * 4; i++)
					printf("%d ", i);
				for (int i = kuai * 2; i >= 1; i--)
					printf("%d%c", i, i == 1 ? '\n' : ' ');
			}
			else
			{
				for (int i = 1; i <= kuai * 2; i++)
					add(i, n);
				for (int i = 1; i <= n; i++)
				{
					for (int j = 1; j <= n; j++)
						printf("%d", mp[i][j]);
					printf("\n");
				}
				for (int i = kuai * 2 + 1; i <= kuai * 4; i++)
					printf("%d ", i);
				for (int i = kuai * 2; i >= 1; i--)
					printf("%d ", i);
				printf("%d\n", n);
			}
		}
		else
		{
			printf("Case #%d: No\n", cas++);
		}
	}
}

G、Is Today Friday?

题意:给你n个格式为yyyy/mm/dd的字符串,让你求一个长度为10的字符串,
意思为第i位的字母为i,求字典序最小的字符串以满足所有给出的字符串
题解:
暴力..next_permutation枚举下一个排列,然后check一下 

#include
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 5;
const int INF = 0x3f3f3f3f;
vector s;
int n, cnt, a[20], tot;
bool flag;
int calc(int y, int m, int d) {
	if (m <= 2)y--, m += 12;
	int c = y / 100; y %= 100;
	int w = (y + y / 4 + c / 4 - 2 * c + 2 * m + 3 * (m + 1) / 5 + d + 1) % 7;
	return (w + 7) % 7;
}
int getIndex(char sh) { return sh - 'A' + 1; }
bool check() {
	bool ff = true;
	for (int i = 0; i < s.size(); ++i) {
		int y, m, d;
		y = a[getIndex(s[i][0])] * 1000 + a[getIndex(s[i][1])] * 100 + a[getIndex(s[i][2])] * 10 + a[getIndex(s[i][3])];
		m = a[getIndex(s[i][5])] * 10 + a[getIndex(s[i][6])];
		d = a[getIndex(s[i][8])] * 10 + a[getIndex(s[i][9])];
		if (y < 1600 || m <= 0 || m >= 13 || d <= 0 || d >= 32)return false;
		if ((m == 4 || m == 6 || m == 9 || m == 11) && d == 31)return false;
		if (m == 2 && d > 28 + (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)))return false;
		if (calc(y, m, d) != 5)return false;
	}
	return true;
}
int main(void)
{
	int t, z = 0;
	scanf("%d", &t);
	while (t--) {
		s.clear();
		++z, cnt = 0, tot = 1, flag = false;
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) { string s1; cin >> s1; s.emplace_back(s1); }
		sort(s.begin(), s.end());
		s.erase(unique(s.begin(), s.end()), s.end());
		for (int i = 1; i <= 10; ++i) a[i] = i - 1, tot *= i;
		for (int i = 1; i <= tot; ++i) {
			if (check()) { flag = true; break; }
			next_permutation(a + 1, a + 11);
		}
		printf("Case #%d: ", z);
		if (flag) {
			for (int i = 1; i <= 10; ++i) printf("%d", a[i]);
			printf("\n");
		}
		else printf("Impossible\n");
	}
	return 0;
}

J、Upgrading Technology

有 n 个技能,每个技能有 m 个等级,初始每个技能等级都是1级,第 i 个技能从等级j-1变成等级 j 需要花费 c_i_j 的金币(可能为负,如果所有等级都到了等级 j ,那么你将获得 d[j](可能为负) 个金币,输出他能获得的金币的最大值。

1、枚举最小能力值,就是最低能获得的d值 

2、维护一个前缀和,对于每一个技能,我们知道他的技能的最小等级,那么我们需要找到最小花费金币,这样才使我们剩余的钱尽量多,考虑建1005颗线段树来维护。

3、对于每颗线段树,我们可以知道这一行能取到的最小的值,考虑一种特殊情况,所有行的最小值都大于我们枚举的最小能力值,显然这种情况是不合法的,因为我们有一段 d 的代价没有加上去,所以我们不考虑这种情况,显然也是不行的,因为他可以少赚取一些金币来使得某个能力值变为我们枚举的最小能力值。所以我们需要记录一下是否有能力到达最小能力值,如果没有的花,我们考虑将算到的答案减去 某个能力值变为最小能力值所少赚取的金币的最小值,就是说我们让一个能力值变为最小能力值,使得多花费的金币尽量少,并且使得这种情况合法。

4、最小能力值从0开始枚举。

5、注意处理一下边界为 0 的情况,由于线段树没有处理 0 的情况,所以需要将求到的最小值与 0 取一下最小值。

#include 
#define lson left,mid,k<<1
#define rson mid+1,right,k<<1|1
#define imid int mid=(left+right)/2;
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
struct node
{
	int l[1005];
	int r[1005];
	ll minn[1005];
}que[1005 * 4];
int n, m, ql, qr;
ll a[1005][1005];
ll d[1005];
void up(int k, int op)
{
	que[k].minn[op] = min(que[k << 1].minn[op], que[k << 1 | 1].minn[op]);
}
void build(int left = 1, int right = m, int k = 1)
{
	for (int i = 1; i <= n; i++)
	{
		que[k].l[i] = left;
		que[k].r[i] = right;
	}
	if (left == right)
	{
		for (int i = 1; i <= n; i++)
			que[k].minn[i] = a[i][left];
		return;
	}
	imid;
	build(lson);
	build(rson);
	for (int i = 1; i <= n; i++)
		up(k, i);
}
ll query(int left, int right, int k, int op)
{
	if (qr < left || right < ql)
		return 1000000000000000000;
	if (ql <= left && right <= qr)
		return que[k].minn[op];
	imid;
	return min(query(lson, op), query(rson, op));
}
int main()
{
	int T, cas = 1;
	sc("%d", &T);
	while (T--)
	{
		ll ans = 0;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				sc("%lld", &a[i][j]);
				a[i][j] += a[i][j - 1];
			}
		}
		for (int i = 1; i <= m; i++)
		{
			sc("%lld", &d[i]);
			d[i] += d[i - 1];
		}
		build();
		for (int i = 0; i <= m; i++)
		{
			bool f = false;
			ll temp = 0;
			ll maxn = -1000000000000000000;
			for (int j = 1; j <= n; j++)
			{
				ll minn = 1000000000000000000;
				if (i == 0)
				{
					ql = 1;
					qr = m;
					minn = min(0LL, query(1, m, 1, j));
				}
				else
				{
					ql = i;
					qr = m;
					minn = query(1, m, 1, j);
				}
				minn -= a[j][i];
				maxn = max(maxn, minn);
				if (minn == 0)
				{
					f = true;
				}
				else
				{
					temp -= minn;
				}
			}
			for (int j = 1; j <= n; j++)
				temp -= a[j][i];
			temp += d[i];
			if (f == true)
			{
				ans = max(ans, temp);
			}
			else
			{
				ans = max(ans, temp + maxn);
			}
		}
		printf("Case #%d: %lld\n", cas++, ans);
	}
}
/*
1
3 3
2 3 -6
2 3 -6
1 1 -6
-100 100 3
*/

 

你可能感兴趣的:(2019,牛客多校赛)