Codeforces Round 890 (Div. 2) supported by Constructor Institute(A~C、E2)---Day13

1856A - Tales of a Sort 

        题意:给定一个长度为n的数组,每次操作令整个数组eq?a_i%20%3D%20max%280%2Ca_i%20-%201%29,求多少次操作以后能够将数组变为有序的。

        思路:若开始并不是有序的,那么必然会存在若干个个“显眼包”夹在一个递增序列和递减序列之间,若这个显眼包不为0,则每次操作过后都不会使得原有序列变为有序的,因此需要将这个显眼包变为0。因此只需要找到所有显眼包当中的最大值即可。

void solve() 
{
	int n;
	cin>>n;
	int a[n];
	for(int i= 0 ; i <n ; i ++)
		cin>>a[i];
	int ans = 0;
	for(int i = 0 ; i < n - 1; i ++){
		if(a[i] > a[i + 1]){
			ans = max(ans,a[i]);
		}
	}
	cout<<ans<<endl;
}        

1856B - Good Arrays 

        题意:给定一个长度为n的正整数数组a,要求判断能否构造出一个正整数数组b,其中满足以下条件:1、eq?%5Csum%20_%7Bi%3D1%7D%5Enai%20%3D%20%5Csum%20_%7Bj%3D1%7D%5Enbj 。 2、eq?%5Cforall%20i%28ai%20%5Cneq%20bi%29

        思路:贪心思想,b中所有数的和是有限的。因此需要将所有的b往小了设置,这样可以为后面的数留有选择余地。将所有eq?b_i%28a_i%20%5Cneq%201%29设为1,eq?b_i%28a_i%3D1%29设为不为1的数,若能够满足,则能够构造出b。

void solve() 
{
	LL n;
	cin>>n;
	LL a[n];
	int cnt1 = 0;
	LL sum = 0;
	for(int i = 0; i < n ; i ++){
		cin>>a[i];
		if(a[i] == 1)
			cnt1++;
		sum += a[i];
	}
	if(n == 1){
		cout<<"NO\n";
		return;
	}
	int res = n - cnt1;
	sum -= res;
	if(sum >= 2 * cnt1){
		cout<<"YES\n";
	}
	else
	{
		cout<<"NO\n";
	}
}   

1856C - To Become Max 

        题意:给定一个长度为n的数组和一个整数k,总共进行k次操作。其中每次操作为:eq?a_i%20%3Da_i%20&plus;%201%28a_i%3C%3Da_%7Bi&plus;1%7D%29,求数组a中所有数的最大值。

        思路:由于每次操作最多能够使得eq?a_i%20%3D%20a_%7Bi&plus;1%7D&plus;1,那么经过k次操作以后,取到最大值的那个数往后必然是一个递减1的序列。由于eq?a_i仅仅和eq?a_%7Bi&plus;1%7D有关联,因此k次操作必然会在一个连续区间内进行操作,在区间以外的操作都不会对答案造成影响。固定区间右端点R,依次往前枚举求最值即可。注意尾部的数不能超过下一个数+1。

例如样例:

5 6

6 5 4 1 5

取[1,4]区间进行处理,首先需要把[1,4]范围内的数变为递减1的序列,通过2次操作后变为[6,5,4,3,5],然后4次操作将整个区间都+1:[7,6,5,4,5],因此答案为7。

在枚举左端点L的时候,可以存一个操作数k:代表了将[L+1,R]区间内的数变为递减1的序列需要多少操作。

void solve() 
{
	int n,k;
	cin>>n>>k;
	LL a[n + 5];
	LL sum[n + 5];
	sum[0] = 0;
	for(int i = 1;  i <= n ; i ++){
		cin>>a[i];
		sum[i] = sum[i - 1] + a[i];
	}
	a[0] = 1e18;
	a[n + 1] = -100;
	LL maxx = 0;
	//12 10 1 100
	//12 10 9
	for(int i = 1 ; i <= n ; i ++)
	{
		LL R_max = max(a[i] , a[i + 1] + 1);
		LL ans = a[i];
		LL pre = 0;
		int kk = a[i];
		for(int j = i - 1 ; j > 0 ; j --){
			LL L_max = a[j - 1];
			int len = i - j + 1;
			LL op = k;
			if(a[j] <= a[i] + len - 1){
				pre += a[i] + len - 1 - a[j];
			}
			else if(a[j] > R_max + len - 1){
				ans = a[j];
				break;
			}
			else{
				LL ss =	(a[i] + a[i] + len - 2) *(len - 1) / 2 + a[j];
				LL target = (a[j] + a[j] - len + 1) * len / 2;
				pre += target - ss;
				a[i] = a[j] - len + 1;
			}
			op -= pre;
			if(op >= 0){
				ans = max(ans , min(R_max + len - 1 , a[i] + len - 1 + op/len));
			}
			else{
				ans = max(a[j] , ans);
				break;
			}
		}
		maxx = max(maxx,ans);
		a[i] = kk;
	}
	cout<<maxx<<endl;
}            

1856E2 - PermuTree (hard version) 

        题意:给定一个从1~n编号的树,其根为1。要求构造出一个排列a,f(a)为数对<u,v>满足eq?a_%7Bu%7D%20%3C%20a_%7Blca%28u%2Cv%29%7D%3Ca_v 的个数。其中eq?a_x代表了x在a中的位置。求f(a)的最大值。

        思路:对于一个父节点而言:其任意两个孩子的编号放在自己编号的两侧即可满足条件。而这两个孩子的子孙也都可以两两满足条件。因此只需要统计每个结点的每颗子树的大小,然后将这些子树分配到自己的两边,其能够构造的数对为两边的子树大小总和相乘。因此问题变成了给定一个数组,将这个数组分为两部分,求两部分相乘的最大值。而最大值问题又变成了将一部分尽可能靠近整个数组总和的二分之一,这就变成了一个多重背包问题。

        下面需要对多重背包问题进行优化:假设整个数组的和为sum。1、若其数组的最大值>=sum/2。则直接将最大值单独拎出来,其余数组成另一部分,这样可以直接求最大值。2、若不存在1的情况,由于sum可以很大(1e6),因此仅仅用普通的背包问题是无法求解的。需要进行压缩,假设有10个大小为1的子树,其能够取到的范围为(0~10)。根据二进制的思想,10 = 1010(B),我们可以将其变为2个1、2个2、1个4,这样能够取到的范围同样为(0~10),其压缩过程如下:如果当前大小的子树数量>=3,在保留至少1个的情况之下(作为二进制位数的1),将其余的都两两合并变成两倍大小。这样便完成了物品的压缩,减少了DP的次数。3、整个过程可以用BITSET来进行进一步的优化,利用位运算的性质来状态转移。(bitset没法传变量,需要进行重写)

struct Bitset {
    #define ull unsigned long long
    #define uint unsigned int 
    #define IV inline void 
    #define IB inline bool
    #define IS inline Bitset
    #define For(i, a, b) for(int i=(a); i <= (b); ++i)
    #define foR(i, a, b) for(int i=(a); i >= (b); --i)
    vector<ull> bit; int len;
    void setN(int N){ int x = N / 64 + 1; bit.resize(x); len = x; for(int i = 0; i < x; ++i) bit[i] = 0;}
    Bitset() = default;
    Bitset(int x){ bit.resize(x); len = x; }
    IV resize(int x) {bit.resize((x >> 6) + 1); len = (x >> 6) + 1;}
    IV set1(int x) {bit[x>>6] |= (1ull<<(x&63));}
    IV set(int x) {bit[x>>6] |= (1ull<<(x&63));}
    IV set0(int x) {bit[x>>6] &= (~(1ull<<(x&63)));}
    IV flip(int x) {bit[x>>6] ^= (1ull<<(x&63));}
    IB operator [] (int x) {return (bit[x>>6] >> (x&63)) & 1;}
    IB any() {For(i, 0, len-1) if(bit[i]) return 1;return 0;}
    IS operator ~ () const {Bitset res(len);For(i, 0, len-1) res.bit[i] = ~bit[i];return res;}
    IS operator | (const Bitset &b) const {Bitset res(len); For(i, 0, len-1) res.bit[i] = bit[i] | b.bit[i];return res;}
    IS operator & (const Bitset &b) const {Bitset res(len); For(i, 0, len-1) res.bit[i] = bit[i] & b.bit[i];return res;}
    IS operator ^ (const Bitset &b) const {Bitset res(len); For(i, 0, len-1) res.bit[i] = bit[i] ^ b.bit[i];return res;}
    IV operator &= (const Bitset &b) {For(i, 0, len-1) bit[i] &= b.bit[i];}
    IV operator |= (const Bitset &b) {For(i, 0, len-1) bit[i] |= b.bit[i];}
    IV operator ^= (const Bitset &b) {For(i, 0, len-1) bit[i] ^= b.bit[i];}
    Bitset operator << (const int t) const {
        Bitset res(len); int high = t >> 6, low = t & 63; ull lst = 0;
        for(int i = 0; i + high < len; i++) {
            res.bit[i + high] = (lst | (bit[i] << low));
            if(low) lst = (bit[i] >> (64 - low));
        }
        return res;
    }
    Bitset operator >> (const int t) const {
        Bitset res(len); int high = t >> 6, low = t & 63; ull lst = 0;
        for(int i = len - 1; i >= high; i--) {
            res.bit[i - high] = (lst | (bit[i] >> low));
            if(low) lst = (bit[i] << (64 - low));
        }
        return res;
    }
    void operator <<= (const int t) {
        int high = t >> 6, low = t & 63;
        for(int i = len - high - 1; ~i; i--) {
            bit[i + high] = (bit[i] << low);
            if(low && i) bit[i + high] |= (bit[i - 1] >> (64 - low));
        }
        for(int i = 0; i < min(high, len - 1); i++) bit[i] = 0;
    } 
    void operator >>= (const int t) {
        int high = t >> 6, low = t & 63;
        for(int i = high; i < len; i++) {
            bit[i - high] = (bit[i] >> low);
            if(low && i != len) bit[i - high] |= (bit[i + 1] << (64 - low));
        }
        for(int i = max(len - high, 0); i < len; i++) bit[i] = 0;
    } 
    void print() {
        foR(i, len-1, 0) foR(j, 63, 0)
            printf("%llu", (bit[i] >> j) & 1);
            printf("@");
        printf("\n");
    }
    #undef IV
    #undef IB
    #undef IS
};
vector<int>tr[N];
vector<LL>g;
LL child[N];
LL out = 0;
LL cnt[N];
void bfs(int root){
	child[root] = 1;
	for(auto x : tr[root]){
		bfs(x);
		child[root] += child[x];
	}
	LL maxx = 0;
	LL sum = 0;
	for(auto x : tr[root]){
		sum += child[x];
		maxx = max(maxx , child[x]);
	}
	if(maxx >= sum/2){
		out += maxx * (sum - maxx);
		return;
	}
	g.clear();
	for(auto x :tr[root]){
		cnt[child[x]]++;
	}
	for(int i = 1 ; i <= sum/2 ; i ++){
		if(cnt[i] >= 3){
			cnt[i * 2] += (cnt[i] - 1)/2;
			if(cnt[i] & 1){
				cnt[i] = 1;
			}
			else{
				cnt[i] = 2;
			}
		}
	}
	LL range = 1;
	for(int i = 1 ; i <= sum/2 ; i ++){
		if(range >= i){
			range += cnt[i] * i;
		}
		for(int j = 0 ; j < cnt[i] ; j ++){
			g.pb(i);
		}
	}
	LL tot = min(sum / 2 , range);
	if(tot != sum / 2){
		Bitset bitset;
		bitset.setN(sum / 2);
		bitset.set1(0);
		for(auto x : g){
			bitset |= (bitset << x);
		}
		for(int i = sum / 2 ; i >tot ;i --){
			if(bitset[i]){
				tot = i;
				break;
			}
		}
	}
	for(int i = 0 ; i <= sum ; i ++){
		cnt[i] = 0;
	}
	out += tot * (sum - tot);
}
void solve() 
{
	int n;
	cin>>n;
	out = 0;
	for(int i = 2 ; i <= n ; i ++){
		int num;
		cin>>num;
		tr[num].pb(i);
	}
	bfs(1);
	cout<<out<<endl;
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
//	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

        

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值