CF 1119 题解

A

发现答案一定至少包含起点或终点中的一个:假设有一种方案不包含,设方案是 \(1 < x < y < n\),因为 \(a_x \neq a_y\),那么一定会有 \(a_x \neq a_1,a_x \neq a_n,a_y \neq a_1,a_y \neq a_n\) 之中的一个(反证即可),所以枚举一下就好了。

B

看错了。。这种不能小块套在大块上。其实每个瓶子下面都需要有隔板。

那么对于每次询问,一定将最大和次大匹配,第三大和第四大匹配。。。。这样就是 \(O(n^2)\) 的。

事实上你可以拿权值线段树维护区间乘,单点改,全局和。可以做到 \(O(n \log n)\)

C

先将两个矩阵异或。这种东西都要找不变的量。

发现不变的量是行列 \(1\) 的个数的奇偶性,所以判断一下是否都是偶数即可。

构造:对于 \(x>1,y>1\),如果这个位置是 \(1\),就操作 \((1,1,x,y)\)。发现操作后只有第一行和第一列可能不是全\(0\)。但是注意到每行每列 \(1\) 的个数都是偶数,所以可以推出都是 \(0\)

D

多次看错题浪费时间。。一开始还以为是严格强于区间不同数字的问题后来才发现对 \(s_i\) 是全局询问。。

一个显然的事实是可以将询问整体平移到 \([0,r-l]\) 上。所以现在相当于处理若干的 \([0,k]\) 询问。

可以发现相当于坐标轴上有 \(n\) 个区间 \([s_i,s_i+k]\),问区间并长度。我们将 \(s_i\) 排序,每次只让 \(s\) 贡献到 \([s_{i},s_{i+1})\) 即可,询问离线后排序,双指针维护出 \(s_{i+1}-s_{i} \leq k\) 的和和数量,就可以快速得到答案。

E

因为二进制的性质,有 \(2^a + 2^b \leq 2^c(a,b < c)\)。可以发现只有两种可能的木棍可以组合:

  • \(a < b = c\)
  • \(a = b = c\)

我们从小到大考虑每一个数,因为前面的木棒只能用来向后匹配了,并且第一种情况耗费的现在可能有用的木棒最少,所以先处理第一种情况再处理第二种情况。

可以考虑 \(>a\) 其实是一个限制,所以我们每次肯定贪心取 \(<b\) 的尽量大的数,这样也可以构造方案了。

看着标签有 FFT 标签,有大佬教我一下怎么 FFT 吗(

F

就差一步想到,我自闭了。

先考虑单次询问怎么做:我们设 \(f_{i,0/1}\) 表示 \(i\) 子树合法的方案数,其中 \(0/1\) 表示父亲边是否割掉。

转移的时候我们先假设全部选择 \(f_{x,0}\),那么从 \(0\) 切换到 \(1\) 的贡献就是 \(-f_{x,0}+w_e+f_{x,1}\)。限制形如至少选 \(x\) 个,如果是负的我们肯定要选,然后才尽量选择最小的。

接下来有一步我没注意到:如果 \(deg_i \leq x\),那么 \(i\) 的所有边可以任意选或不选

于是我们从小到大计算 \(x\) 的答案,对每个点维护 multiset 表示切掉一条边的所有代价,每次将所有 \(deg_i = x\) 的点删除(删除形如将这个点所有相邻的点的 multiset 都加入一个边权),然后对森林每个连通块 dp 即可。分析一下我们总遍历的点数:每个点只会在 $ < deg_i$ 的地方被遍历,所以总点数是 \(O(\sum deg_i) = O(n)\) 的。但是这里在每个点求前 \(k\) 大的和的时候不能每次扫一遍否则会变成 \(O(\sum deg_i^2)\),所以我们维护两个 multiset 分别存前 \(k\) 小和剩余的元素,每次要询问的时候就暴力调整,通过均摊分析可以证明对于每个点 \(i\) 形如前 \(k\) 大的询问单调从 \(deg_i\) 降到 \(0\),每次降低都会贡献 \(O( \log n)\) 的复杂度,所以总复杂度 \(O(n \log n)\)

这种题还是要考虑复杂度是否能和 \(\sum deg_i\) 这种东西扯上关系吧。。

G

神仙构造题。

首先答案的一个下界显然是 \(\lceil \frac{\sum hp_i}{n}\rceil\),也就是每个士兵都不浪费(除了在打最后一个怪的时候),那么我们提出如下策略:

从前往后考虑所有怪,每次如果这个怪血量 \(\geq n\) 就让所有士兵去打怪,否则设剩余的血量为 \(x\),分出若干组士兵,总人数为 \(x\) 去打这个怪,剩下的士兵同时去考虑下一个怪。

考虑这个过程:如果我们设前缀和为 \(s_i\),那么相当于限制了你的划分方案能表示出 \(s_i \bmod n\),一种构造方法是排序后设 \(d_i = s_{i}-s_{i-1}\),然后发现前缀和就可以表示出所有的数字了。

构造就顺着往下打就可以,因为这种方案是前缀和表示出怪,所以一定是顺着打的。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e6 + 5;
int n,m;
int a[MAXN];
int b[MAXN];

int main(){
	scanf("%d%d",&n,&m);
	FOR(i,1,m) scanf("%d",a+i);
	int sm = 0;std::vector<int> v1;v1.pb(0);v1.pb(n);
	FOR(i,1,m-1){
		sm += a[i];
		v1.pb(sm%n);
	}
	sm += a[m];
	std::sort(all(v1));std::vector<int> sz;
	FOR(i,1,(int)v1.size()-1) sz.pb(v1[i]-v1[i-1]);
	printf("%d\n",(sm+n-1)/n);
	for(auto x:sz) printf("%d ",x);puts("");
	int p = 0;
	FOR(i,1,m){
		while(a[i] > 0){
			a[i] -= sz[p];++p;
			printf("%d ",i);
			if(p == (int)sz.size()) puts(""),p = 0;
		}
	}
	if(p) FOR(i,p,m-1) printf("1 ");
	puts("");
	return 0;
}

H

又是神仙 \(FWT\) +性质题。。

设集合幂级数 \(F_i(s) = xs^{a_i}+ys^{b_i}+zs^{c_i}\),那么答案就是 \(\prod_{i=1}^n F_i\),卷积定义为对称差卷积,这里记异或符号为 \(\oplus\)

做这种题肯定是要 \(FWT\) 的,但是直接 \(FWT\) 时间显然不行,对于这种题我们考虑将 \(FWT\) 后的式子列出来观察是否能优化。

考虑算 \(S\) 处的答案,那么 \([s^S]FWT(F_i) = xs^{|a_i \cap S|}+ys^{|b_i \cap S|} + zs^{|c_i \cap S|}\)

\([s^S]FWT(ANS) = \prod_{i=1}^n (xs^{|a_i \cap S|}+ys^{|b_i \cap S|} + zs^{|c_i \cap S|})\),直接计算仍然过不了,但是我们发现这个括号内的式子只有 \(8\) 种可能。

但是这样还是太多了,我们考虑初始将所有 \(a_i,b_i,c_i\) 都异或上 \(a_i\),那么答案就异或上了 \(\oplus_{i=1}^n a_i\)

现在只有四种可能了:也就是 \(x+y+z,x+y-z,x-y+z,x-y-z\),设这四种可能的方案数分别为 \(c_1,c_2,c_3,c_4\),如果能求出方案数就可以快速幂计算出 \(FWT(ANS)\) 的每一项系数,\(IFWT\) 回去就做完了。

我们考虑解方程:首先显然有 \(c_1 + c_2 + c_3 + c_4 = n\)

我们考虑 \([s^S]\sum_{i=1}^n FWT(F_i)\),注意到这里的 \(x,y,z\) 是可以随便取的,但是取 \(x\) 已经没什么用了,于是我们如果取 \(x=0,y=1,z=0\),也是有 \(p_1 = [s^S]\sum_{i=1}^n FWT(F_i) = \sum_{i=1}^n (-1)^{|b_i \cap S|} = c_1+c_2-c_3-c_4\),想要计算 \([s^S]\sum_{i=1}^n FWT(F_i)\) 显然不能暴力,注意到 \(FWT\) 是一个线性运算(本质是左乘转移矩阵),所以可以直接计算 \(FWT(\sum_{i=1}^n s^{b_i})\)

同样的 ,如果我们取 \(x=0,y=0,z=1\),那么就有 \(p_2 = c_1-c_2+c_3-c_4\)

现在因为已经取了 \((0,1,0),(0,0,1)\),那么取 \(y,z\) 的线性组合已经没用了,我们要找到一个不一样的等量。

考虑上面两种情况的对称差卷积的和,也就是 \(p_3=[s^S]\sum_{i=1}^n FWT(s^{b_i \oplus c_i}) = \sum_{i=1}^n (-1)^{|b_i \cap S|} (-1)^{|c_i\cap S|}\),(对称差卷积等价于 \(FWT\) 点乘)也就有 \(p_3 = c_1-c_2-c_3+c_4\)

这样有方程组:

\[\begin{aligned} \begin{cases} c_1+c_2+c_3+c_4 = n\\ c_1 +c_2-c_3-c_4=p_1\\ c_1-c_2+c_3-c_4=p_2\\ c_1-c_2-c_3+c_4=p_3 \end{cases} \Rightarrow \begin{cases} c_1 = \frac{n+p_1+p_2+p_3}{4}\\ c_2 = \frac{n+p_1-2c_1}{2}\\ c_3 = \frac{n+p_2-2c_1}{2}\\ c_4 = \frac{n+p_3-2c_1}{2}\\ \end{cases} \end{aligned} \]

就可以求出 \(FWT(ANS)\) 所有的点值,就可以计算答案了。时间复杂度 \(O(k2^k +2^k\log n)\)

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

int N;
const int MAXN = 2e5 + 5;
const int ha = 998244353;
const int inv2 = 499122177;
const int inv4 = 748683265;

inline int qpow(LL a,int n=ha-2){
	a = (a%ha+ha)%ha;
	int res = 1;
	while(n){
		if(n & 1) res = 1ll*res*a%ha;
		a = 1ll*a*a%ha;
		n >>= 1;
	}
	return res;
}

inline void FWT(int f[]){
	for(int mid = 1;mid < N;mid <<= 1){
		for(int i = 0;i < N;i += (mid<<1)){
			FOR(j,0,mid-1){
				int x = f[i+j],y = f[i+mid+j];
				f[i+j] = (x+y)%ha;f[i+mid+j] = (x+ha-y)%ha;
			}
		}
	}
}

inline void iFWT(int f[]){
	for(int mid = N>>1;mid;mid >>= 1){
		for(int i = 0;i < N;i += (mid<<1)){
			FOR(j,0,mid-1){
				int x=  f[i+j],y = f[i+mid+j];
				f[i+j] = 1ll*(x+y)*inv2%ha;f[i+mid+j] = 1ll*(x+ha-y)*inv2%ha;
			}
		}
	}
}

int n,k;
int a[MAXN],b[MAXN],c[MAXN];
int sm = 0;
int f1[MAXN],f2[MAXN],f3[MAXN],ans[MAXN];

int main(){
	scanf("%d%d",&n,&k);N = 1<<k;
	int x,y,z;scanf("%d%d%d",&x,&y,&z);
	x %= ha;y %= ha;z %= ha;
	FOR(i,1,n){
		scanf("%d%d%d",a+i,b+i,c+i);
		sm ^= a[i];b[i] ^= a[i];c[i] ^= a[i];a[i] = 0;
		++f1[b[i]];++f2[c[i]];++f3[b[i]^c[i]];
	}
	FWT(f1);FWT(f2);FWT(f3);
	FOR(i,0,N-1){
		LL p1 = f1[i],p2 = f2[i],p3 = f3[i];
		LL c1 = 1ll*(n+p1+p2+p3)%ha*inv4%ha;
		LL t = 2ll*c1%ha;t = ha-t;
		LL c2 = 1ll*(n+p1+t)%ha*inv2%ha;
		LL c3 = 1ll*(n+p2+t)%ha*inv2%ha;
		LL c4 = 1ll*(n+p3+t)%ha*inv2%ha;
		ans[i] = 1ll*qpow(x+y+z,c1)*qpow(x+y-z,c2)%ha*qpow(x-y+z,c3)%ha*qpow(x-y-z,c4)%ha;
	}
	iFWT(ans);
	FOR(i,0,N-1) printf("%d ",ans[i^sm]);
	return 0;
}
posted @ 2020-09-28 22:24  RainAir  阅读(71)  评论(0编辑  收藏  举报