• CF Round #510 (Div. 2)


    前言:没想到那么快就打了第二场,题目难度比CF Round #509 (Div. 2)这场要难些,不过我依旧菜,这场更是被(D)题卡了,最后(C)题都来不及敲了。。最后才(A)(3)题,幸好(Rating)没掉。

    A. Benches

    Description

    (n)个位置,给出每个位置上原本有(a[i])个人。现有(m)个人,把他们安排到这(n)个位置上,设安排完后位置上人数最多的位置有(k)个人,求最大的(k)和最小的(k)

    Solution

    官方正解复杂度为(O(mn))
    而我机房里有大佬写了二分答案的做法(传送门
    这里给出我的(O(n))做法。
    最大值显然就是(n)个位置上原有的最多的人数加上(m)
    对于最小值,其实就是去填(n)个位置,使得每个位置的人数尽量平均,设原有最多人数为(maxn),则一共可以填(sumlimits_{i=1}^n maxn-a[i])个人。
    若填完后没有剩余,则答案就是(maxn);若有剩余,则将剩余的人平均分配到(n)个位置即可。

    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int N = 110;
    int a[N];
    inline int re()
    {
    	int x = 0;
    	char c = getchar();
    	bool p = 0;
    	for (; c < '0' || c > '9'; c = getchar())
    		p |= c == '-';
    	for (; c >= '0' && c <= '9'; c = getchar())
    		x = x * 10 + c - '0';
    	return p ? -x : x;
    }
    inline int maxn(int x, int y)
    {
    	return x > y ? x : y;
    }
    int main()
    {
    	int i, n, m, ma = 0, s = 0;
    	n = re();
    	m = re();
    	for (i = 1; i <= n; i++)
    	{
    		a[i] = re();
    		ma = maxn(ma, a[i]);
    	}
    	for (i = 1; i <= n; i++)
    		s += ma - a[i];
    	if (s >= m)
    		printf("%d %d", ma, ma + m);
    	else
    		printf("%d %d", ma + (int)ceil(1.0 * (m - s) / n), ma + m);
    	return 0;
    }
    

    B. Vitamins

    Description

    (n)杯果汁,每个果汁需要花费(a[i]),同时会提供维生素,维生素有(ABC)三种类型,而每种果汁会提供其中的几种维生素,而(Petya)希望获得(ABC)三种维生素,求达成目标的最小花费。

    Solution

    我机房里的大佬写的和正解是一样的(传送门
    而我写了个记忆化搜索,算是玄学复杂度,不过也能过。
    对于维生素的储存可以使用压位的方法,并在搜索中记录在已经获得某些维生素的最小花费即可。

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N = 1010;
    int a[N], b[N], f[10], n;
    inline int re()
    {
    	int x = 0;
    	char c = getchar();
    	bool p = 0;
    	for (; c < '0' || c > '9'; c = getchar())
    		p |= c == '-';
    	for (; c >= '0' && c <= '9'; c = getchar())
    		x = x * 10 + c - '0';
    	return p ? -x : x;
    }
    inline char re_l()
    {
    	char c = getchar();
    	for (; c != 'A' && c != 'B' && c != 'C' && c != '
    ' && c != '
    ' && c > 0; c = getchar());
    	return c;
    }
    void dfs(int x, int nw, int se)
    {
    	int i;
    	if (nw > f[se])
    		return;
    	f[se] = nw;
    	for (i = x; i <= n; i++)
    		if ((se | b[i]) ^ se)
    			dfs(i + 1, nw + a[i], se | b[i]);
    }
    int main()
    {
    	int i, k = 0;
    	char c;
    	n = re();
    	for (i = 1; i <= n; i++)
    	{
    		a[i] = re();
    		while (1)
    		{
    			c = re_l();
    			if (c != 'A' && c != 'B' && c != 'C')
    				break;
    			if (c == 'A')
    				b[i] |= 1;
    			else
    				if (c == 'B')
    					b[i] |= 1 << 1;
    				else
    					b[i] |= 1 << 2;
    		}
    		k |= b[i];
    	}
    	if (k ^ 7)
    	{
    		printf("-1");
    		return 0;
    	}
    	memset(f, 60, sizeof(f));
    	dfs(1, 0, 0);
    	printf("%d", f[7]);
    	return 0;
    }
    

    C. Array Product

    Description

    给出一个有(n)项的序列(a), 有两种操作

    1. 选择两个数(a_i, a_j),把(a_j)更新为(a_i imes a_j), 将(a_i)删去
    2. 直接删去数(a_i) ( 该种操作最多只能进行(1)次)

    求如何操作才能使(n-1)次操作后剩下的那个数最大, 输出操作的方案。

    Solution

    比赛时因为被(D)题卡了,所以没来得及打,赛后才(A)的。
    显然正数可以忽略,考虑负数和(0),有(4)种情况。

    1. 奇数个负数,存在(0)。将(0)和绝对值最小的负数相乘,才删去得到的(0),剩下的全部乘起来。
    2. 奇数个负数,不存在(0)。直接删去(0),剩下的全部乘起来。
    3. 偶数个负数,存在(0)。将所有(0)乘起来并删去最后得到的(0),剩下的全部乘起来。
    4. 偶数个负数,不存在(0)。直接全部乘起来。
    #include<cstdio>
    #include<cstring>
    #include<set>
    #include<algorithm>
    using namespace std;
    const int N = 2e5 + 10;
    int ze[N], zer, k;
    set<int>se;
    inline int re()
    {
    	int x = 0;
    	char c = getchar();
    	bool p = 0;
    	for (; c < '0' || c > '9'; c = getchar())
    		p |= c == '-';
    	for (; c >= '0' && c <= '9'; c = getchar())
    		x = x * 10 + c - '0';
    	return p ? -x : x;
    }
    void dze()
    {
    	int i;
    	for (i = 2; i <= zer; i++)
    	{
    		se.erase(ze[i - 1]);
    		printf("1 %d %d
    ", ze[i - 1], ze[i]);
    	}
    	if (se.size() ^ 1)
    	{
    		se.erase(ze[zer]);
    		printf("2 %d
    ", ze[zer]);
    	}
    }
    void alc()
    {
    	int x, s = se.size();
    	while (s ^ 1)
    	{
    		set<int>::iterator it = se.begin();
    		x = *it;
    		++it;
    		printf("1 %d %d
    ", x, *it);
    		se.erase(x);
    		s--;
    	}
    }
    int main()
    {
    	int i, x, n, ma = -1e9, o, fsh = 0;
    	n = re();
    	for (i = 1; i <= n; i++)
    	{
    		x = re();
    		if (x < 0)
    		{
    			++fsh;
    			if (ma <= x)
    			{
    				ma = x;
    				o = i;
    			}
    		}
    		else
    			if (!x)
    				ze[++zer] = i;
    		se.insert(i);
    	}
    	if (fsh & 1 && zer)
    	{
    		printf("1 %d %d
    ", o, ze[zer]);
    		se.erase(o);
    		dze();
    	}
    	else
    		if (fsh & 1)
    		{
    			printf("2 %d
    ", o);
    			se.erase(o);
    		}
    		else
    			if (zer)
    				dze();
    	alc();
    	return 0;
    }
    

    D. Petya and Array

    Description

    给出一个含(n)个数的序列(a),和一个数(t),求序列中有多少区间满足(sumlimits_{i=l}^{r}a_i<t)

    Solution

    求一个后缀和,将问题转化为有多少个子段满足(S[j]-S[i+1]<t ightarrow S[j]<t+S[i+1]),枚举(i),前面的和用数据结构维护即可。
    机房大佬写的是求前缀和,再用树状数组求逆序对维护,道理是差不多的(传送门
    而我比较菜,直接去把我的平衡树板子复制过来,实时插入并求排名来维护。
    不过我的(Splay)板子出了奇怪的锅,导致在这题上浪费很多时间,还未通过(4)次,最后一气之下换了(Treap)板子,然后才(A)了。。
    另外,因为我写平衡树板子的时候没有打空格的习惯,所以代码显得很奇怪。。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 10;
    struct trp{
    	ll l,r,s,v,si,rd;
    };
    trp tr[N];
    ll S[N], ro, nu, an;
    int a[N];
    inline ll re()
    {
    	ll x = 0;
    	char c = getchar();
    	bool p = 0;
    	for (; c < '0' || c > '9'; c = getchar())
    		p |= c == '-';
    	for (; c >= '0' && c <= '9'; c = getchar())
    		x = x * 10 + c - '0';
    	return p ? -x : x;
    }
    void pp(ll r)
    {
    	tr[r].si=tr[tr[r].l].si+tr[tr[r].r].si+tr[r].s;
    }
    void rt(ll& r)
    {
    	ll k=tr[r].l;
    	tr[r].l=tr[k].r;
    	tr[k].r=r;
    	tr[k].si=tr[r].si;
    	pp(r);
    	r=k;
    }
    void lt(ll& r)
    {
    	ll k=tr[r].r;
    	tr[r].r=tr[k].l;
    	tr[k].l=r;
    	tr[k].si=tr[r].si;
    	pp(r);
    	r=k;
    }
    void is(ll& r,ll x)
    {
    	if(!r)
    	{
    		r=++nu;
    		tr[r].v=x;
    		tr[r].s=tr[r].si=1;
    		tr[r].rd=rand();
    		return;
    	}
    	tr[r].si++;
    	if(tr[r].v==x)
    		tr[r].s++;
    	else
    	{
    		if(tr[r].v<x)
    		{
    			is(tr[r].r,x);
    			if(tr[tr[r].r].rd<tr[r].rd)
    				lt(r);
    		}
    		else
    		{
    			is(tr[r].l,x);
    			if(tr[tr[r].l].rd<tr[r].rd)
    				rt(r);
    		}
    	}
    }
    void qu_fo(ll r,ll x)
    {
    	if(!r)
    		return;
    	if(tr[r].v<x)
    	{
    		an=r;
    		qu_fo(tr[r].r,x);
    	}
    	else
    		qu_fo(tr[r].l,x);
    }
    ll qu_rk(ll r,ll x)
    {
    	if(!r)
    		return 0;
    	if(tr[r].v==x)
    		return tr[tr[r].l].si+tr[r].s;
    	if(tr[r].v<x)
    		return tr[tr[r].l].si+tr[r].s+qu_rk(tr[r].r,x);
    	return qu_rk(tr[r].l,x);
    }
    ll qu_nu(ll r,ll x)
    {
    	if(!r)
    		return 0;
    	if(x<=tr[tr[r].l].si)
    		return qu_nu(tr[r].l,x);
    	else
    		if(x>tr[tr[r].l].si+tr[r].s)
    			return qu_nu(tr[r].r,x-tr[tr[r].l].si-tr[r].s);
    	return tr[r].v;
    }
    int main()
    {
    	srand(20021113);
    	int i, n;
    	ll m, qj = 0, j;
    	n = re();
    	m = re();
    	for (i = 1; i <= n; i++)
    		a[i] = re();
    	for (i = n; i; i--)
    		S[i] = S[i + 1] + a[i];
    	for (i = 1; i <= n; i++)
    	{
    		is(ro, S[i]);
    		an = -1e18;
    		qu_fo(ro, m + S[i + 1]);
    		if (an == -1e18)
    			continue;
    		j = tr[an].v;
    		qj += qu_rk(ro, j);
    	}
    	printf("%I64d", qj);
    	return 0;
    }
    
  • 相关阅读:
    SkinSharp用法
    nosql和关系型数据库比较?
    Java实现 蓝桥杯VIP 算法提高 进制转换
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 3-2字符串输入输出函数
    Java实现 蓝桥杯VIP 算法提高 去注释
    Java实现 蓝桥杯VIP 算法提高 去注释
  • 原文地址:https://www.cnblogs.com/Iowa-Battleship/p/9664937.html
Copyright © 2020-2023  润新知