Codeforces Global Round 19

G.Birthday

题目描述

点此看题

解法

可以写个暴力跑出 n=3,4,5,6,7... 的情况,猜测最后的结果是最小的 x 满足它是 2 的幂次且 xn

n=x,则可以让 nn1,这样答案不变,并且可以保证 n<x

有一个简单的构造想法是把 i(i>x2)xi 配对,就可以得到 x2ix,我们把能配对的都配对了,留下得到的若干个 x,会剩下这些东西:

  • 1,2...nx1
  • 2,4...2(nx2)
  • 单独的 x2

设原来的问题为 (n,x,k),其中 k 表示每个数乘上的倍数,那么我们得到了 (nx1,x2,k)(nx2,x2,2k) 这两个子问题,可以直接递归下去。但是递归回来后还有一个问题,就是要把 x2 变成 x 才能回溯。

发现对于 n7 的情况,最后一步都是 (0,x),而 0 可以完成下面神奇的操作:

  • 操作 (0,a),变成了 (a,a)
  • 操作 (a,a),变成了 (0,2a)

也就是 0 在保证自身不变的情况下,可以倍增一个数。那么我们的问题都撤销最后一步,这样就可以留下一个 0

再来梳理一遍,对于 n7 我们直接取用暴搜的结果;对于 n8,我们可以直接递归下去,由于一定有一个子问题满足 n3,所以回溯时一定能获取一个 0,用这个 0 倍增所有 <x 数就可以回溯了。

操作次数 O(nlogn),讲解没完全看懂的可以参考代码。

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define pii pair<int,int> 
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
vector<pii> b[8] = {
	{},
	{},
	{},
	{{1, 3}, {2, 2}},
	{{1, 3}, {2, 2}},
	{{1, 2}, {1, 3}, {2, 4}, {2, 6}, {4, 4}, {3, 5}, {0, 2}, {2, 2}, {0, 4}, {4, 4}},
	{{1, 2}, {1, 3}, {2, 4}, {2, 6}, {4, 4}, {3, 5}, {2, 6}, {0, 4}, {4, 4}},
	{{1, 2}, {3, 5}, {2, 6}, {4, 4}, {1, 0}, {1, 3}, {1, 7}, {2, 6}, {4, 4}}
};
int T,n,m;vector<pii> a;
void add(int x,int y,int k)
{
	a.push_back({x*k,y*k});
}
void get(int x,int to,int k)
{
	while(x<to)
	{
		add(0,x,k);
		add(x,x,k);
		x<<=1;
	}
}
void dfs(int n,int x,int k)
{
	if(n==x) {dfs(n-1,x,k);return ;}
	if(n<=x/2)
	{
		dfs(n,x/2,k);
		for(int i=1;i<n;i++) get(x/2,x,k);
		return ;
	}
	if(n<=7)
	{
		for(auto [x,y]:b[n]) add(x,y,k);
		return ;
	}
	for(int i=x/2+1;i<=n;i++) add(x-i,i,k);
	int A=x-n-1,B=n-x/2;
	if(A>=3)
	{
		dfs(A,x/2,k);
		for(int i=1;i<A;i++) get(x/2,x,k);
	}
	if(B>=3) dfs(B,x/2,k*2);
	get(x/2,x,k);
	if(A<=2) for(int i=1;i<=A;i++) get(i,x,k);
	if(B<=2) for(int i=1;i<=B;i++) get(i,x/2,k*2);
	if(A>=3 && B>=3) add(0,x,k);
}
void work()
{
	n=read();m=1;a.clear();
	if(n==2) {puts("-1");return ;}
	while(m<n) m<<=1;
	if(n<=7) a=b[n];
	else dfs(n,m,1);
	add(0,m,1);
	printf("%d\n",a.size());
	for(auto [x,y]:a) printf("%d %d\n",x,y);
}
signed main()
{
	T=read();
	while(T--) work();
}

H.Minimize Inversions Number

题目描述

点此看题

解法

首先考虑 k=1 的情况,设 di=j=1i1[pj>pi][pj<pi],表示如果前移 i 逆序对的减少量。

对于 k>1 的情况,直接用选取点的 di 计算代价是不行的,需要减去一个「选取子序列的顺序对数 逆序对数」的修正量。设 s1,s2...sk 表示选取的子序列,q1,q2...qk 表示对应的值,那么减去的总量是:

i=1kdsi+(k2)2i=1kj=1i1[qj>qi]

所以我们要最大化 i=1kdsi2i=1kj=1i1[qj>qi]

关键的结论是:对于原序列的逆序对 (i,j),如果 i 在子序列中,那么 j 一定要在子序列中。

证明使用反证 + 调整法,即证明存在方案不劣于 i 在子序列中,但是 j 不在子序列中。

我们从 存在逆序对 (i,j),满足 i 在子序列中,但是 j 不在子序列中 开始调整。考虑收紧限制,找到 ji 最小并且满足上述条件的逆序对 (i,j),因为 ji 最小,所以在 (i,j) 之间不存在这样的数:

  • 值介于 pipj 之间的数。
  • 大于 pi 且在子序列中的数。
  • 小于 pj 且不在子序列中的数。

考虑取消 i 的选择,转而选择 j,考虑代价的变化,对于某个位置 k 的贡献是:

  • 如果 k>j 且不在子序列中,由于相对位置不变,所以无贡献。
  • 如果 k>j 且在子序列中,当且仅当 pk 介于 pi,pj 之间会有 2 的贡献,否则无贡献。
  • 如果 k<i 且在子序列中,由于相对位置不变,所以无贡献。
  • 如果 k<i 且不在子序列中,当且仅当 pk 介于 pi,pj 之间会有 2 的贡献,否则无贡献。
  • 如果 i<k<jpk>pi 且不在子序列中,贡献为 1
  • 如果 i<k<jpk<pj 且在子序列中,贡献为 1

所以总贡献非正,并且由于调整可以在有限步内结束,所以 i 在子序列中,但是 j 不在子序列中 不会成为最优解。

如果感性理解上面的结论,就是决定逆序对的只有:位置和值。那么位置大的并且值小的放前面更优,所以原序列的逆序对会导致一些选择的偏序关系,可以指向上面的结论。

知道了这个结论后,我们只需要最大化 i=1kdsi2i=1kj=si+1n[pi>pj]

所以预处理 ci=di2j=i+1n[pi>pj],然后按照 c 从大到小选择即可,时间复杂度 O(nlogn)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 500005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,ans,a[M],b[M],c[M];
void add(int x)
{
	for(int i=x;i<=n;i+=i&(-i)) b[i]++;
}
int ask(int x)
{
	int r=0;
	for(int i=x;i>0;i-=i&(-i)) r+=b[i];
	return r;
}
void work()
{
	n=read();ans=0;
	for(int i=1;i<=n;i++) a[i]=read(),b[i]=0;
	for(int i=1;i<=n;i++)
	{
		int d=ask(a[i]);
		ans+=i-1-d;c[i]=i-1-2*d;
		add(a[i]);
	}
	for(int i=1;i<=n;i++) b[i]=0;
	for(int i=n;i>=1;i--)
		c[i]-=2*ask(a[i]),add(a[i]);
	sort(c+1,c+1+n,greater<int>());
	printf("%lld ",ans);
	for(int i=1;i<=n;i++)
	{
		ans-=c[i]+(i-1);
		printf("%lld ",ans);
	}
	puts("");
}
signed main()
{
	T=read();
	while(T--) work();
}
posted @   C202044zxy  阅读(97)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-07-03 CF1458D Flip and Reverse
点击右上角即可分享
微信分享提示