• CF 1119 题解


    A

    发现答案一定至少包含起点或终点中的一个:假设有一种方案不包含,设方案是 (1 < x < y < n),因为 (a_x eq a_y),那么一定会有 (a_x eq a_1,a_x eq a_n,a_y eq a_1,a_y eq 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} ceil),也就是每个士兵都不浪费(除了在打最后一个怪的时候),那么我们提出如下策略:

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

    考虑这个过程:如果我们设前缀和为 (s_i),那么相当于限制了你的划分方案能表示出 (s_i mod 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
    ",(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_icap S|}),(对称差卷积等价于 (FWT) 点乘)也就有 (p_3 = c_1-c_2-c_3+c_4)

    这样有方程组:

    [egin{aligned} egin{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 egin{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^klog 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;
    }
    
  • 相关阅读:
    画了朵花
    定位
    浮动
    盒模型
    html+css笔记1
    闭包
    高阶函数
    函数
    Map Set iterable
    git stash clear/drop 后代码如何恢复
  • 原文地址:https://www.cnblogs.com/rainair/p/14305852.html
Copyright © 2020-2023  润新知