Codechef May Challenge 2020 简要题解

这次赛时没打,赛后补了下题目,被最后一题打爆,特意学了一波相关姿势。Triple Sort略Chef and Bitwise Product略Sorting Vases显然MMM个交换的意义就是将位置划分为若干等价类。那么可以将最初的每个数iii写成(ui,vi)(u_i,v_i)(ui​,vi​)的形式,代表一开始在等价类uiu_iui​,最后要到等价类viv_ivi​。考虑将数字间的交换当做一条边,那么得到的每个弱连通分量需要满足每个等价类在uuu中出现次数等于在vvv中出现次数。并且对于
摘要由CSDN通过智能技术生成

这次赛时没打,赛后补了下题目,被最后一题打爆,特意学了一波相关姿势。

Triple Sort

Chef and Bitwise Product

Sorting Vases

显然 M M M个交换的意义就是将位置划分为若干等价类。那么可以将最初的每个数 i i i写成 ( u i , v i ) (u_i,v_i) (ui,vi)的形式,代表一开始在等价类 u i u_i ui,最后要到等价类 v i v_i vi
考虑将数字间的交换当做一条边,那么得到的每个弱连通分量需要满足每个等价类在 u u u中出现次数等于在 v v v中出现次数。并且对于一个大小为 k k k的弱连通分量,至少需要交换 k − 1 k-1 k1次,那么假设 N N N个数最多能划分为 w w w个合法的可以作为弱连通分量集合的非空集合,答案下界即为 N − w N-w Nw,并且容易达到这个下界。
现在问题是求出 w w w,直接枚举集合的子集DP或是暴力做 N N N次子集卷积复杂度分别为 O ( 3 N ) \mathcal O(3^N) O(3N) O ( 2 N ⋅ N 2 ) \mathcal O(2^N\cdot N^2) O(2NN2),都不好通过。考虑关注一下性质,显然若干个合法的不交集合的并也是合法的,那么考虑已经求出了所有的能够划分为 i i i个合法非空集合的集合,对于每个集合 S S S,它能够划分为 i + 1 i+1 i+1个非空集合当且仅当它自身是合法的且存在一个 T ⊆ S T \subseteq S TS使得 T T T能够划分为 i i i个非空集合,做 O ( N ) O(N) O(N)次高维前缀和即可。
单组数据时间复杂度为 O ( 2 N ⋅ N 2 ) \mathcal O(2^N\cdot N^2) O(2NN2)

#include <bits/stdc++.h>

using namespace std;

namespace SETS {
   

int fa[20];

void init(int n) {
   
  for(int i=1;i<=n;i++) fa[i]=i;
}

int find_father(int x) {
   
  return (fa[x]==x)?x:fa[x]=find_father(fa[x]);
}

void merge(int x,int y) {
   
  x=find_father(x);y=find_father(y);
  if (x==y) return;
  fa[x]=y;
}

}

int p[20];
int col[20],cnt[20];

bool f[1<<18];

void pre(int n) {
   
  for(int i=1;i<(1<<n);i++) {
   
  	memset(cnt,0,sizeof(cnt));
  	for(int j=1;j<=n;j++)
  	  if ((i>>(j-1))&1) {
   
  	  	  cnt[col[j]]++;
  	  	  cnt[col[p[j]]]--;
		}
	bool ok=1;
	for(int j=1;j<=n;j++)
	  if (cnt[j]) {
   
	  	ok=0;
	  	break;
	  }
	f[i]=ok;
  }
}

void fwt(int *p,int len) {
   
  for(int i=1;i<len;i<<=1)
    for(int j=0;j<len;j++)
      if (j&i) p[j]+=p[j^i];
}

int g[1<<18];

int main() {
   
  int cases;
  scanf("%d",&cases);
  for(;cases;cases--) {
   
  	int n,m;
  	scanf("%d%d",&n,&m);
  	for(int i=1;i<=n;i++) scanf("%d",&p[i]);
  	SETS::init(n);
  	for(int i=1;i<=m;i++) {
   
  		int x,y;
  		scanf("%d%d",&x,&y);
  		SETS::merge(x,y);
	  }
	for(int i=1;i<=n;i++) col[i]=SETS::find_father(i);
	pre(n);
	int ans=n-1;
	for(;;) {
   
		for(int i=0;i<(1<<n);i++) g[i]=f[i];
		fwt(g,1<<n);
		for(int i=0;i<(1<<n);i++) f[i]=(f[i]&&g[i]>1);
		if (!f[(1<<n)-1]) break;
		ans--;
	}
	printf("%d\n",ans);
  }
  return 0;
}
/*
1
6 0
2 1 4 5 3 6
*/

Buying a New String

Binary Land

经典的双栈模拟队列。
考虑没有删除的做法,显然可以维护一个 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k]起点在 ( 1 , k ) (1,k) (1,k),终点在 ( i , j ) (i,j) (i,j)的路径数目,转移直接计算即可。
再考虑有删除的做法,注意到我们可以快速合并两个 D P DP DP数组,那么我们随便选一行 m i d mid mid,维护两个DP数组 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k] G [ i ′ ] [ j ′ ] [ k ′ ] G[i'][j'][k'] G[i][j][k],分别表示起点在 ( m i d + 1 , k ) (mid+1,k) (mid+1,k),终点在 ( i , j ) (i,j) (i,j)和起点在 ( i ′ , j ′ ) (i',j') (i,j),终点在 ( m i d , k ) (mid,k) (mid,k)的路径数目,查询的时候可以直接合并。插入操作就在 F F F最后加入一行,删除操作如果没有删空 m i d mid mid前面的就直接删,否则取 m i d mid mid为当前最后一行重

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值