2023.11.14

CLYZ 联考。鉴定为 FJOI。

A

\(\{1,2,\dots,n\}\) 有多少子集的 \(\gcd=G\)\(\operatorname{lcm}=L\)

另外地,多次询问若子集包含 \(x\) 的方案数。答案模 \(998244853\)

\(1\le n,G,L\le 10^8\)\(1\le q\le 5000\)\(1\le x\le n\)

\(\mathrm{TL}=6\mathrm{s}\)


先解决第一个问题。

\(G\not\mid L\) 显然答案为 \(0\)。否则令 \(L\leftarrow \dfrac{L}{G}\)\(n\leftarrow \left\lfloor\dfrac{n}{G}\right\rfloor\)

也就是说 \(\gcd=1\)\(\operatorname{lcm}=L\)。显然的是只有 \(L\) 的因子有贡献。

注意到 \(\omega(L)\le 8\),考虑状压。

\(f_{i,S,T}\) 表示当前 \(S\) 集合内的质因子顶到上界,\(T\) 集合内的质因子顶到下界的方案数。

转移为 \(f_{i,S|s,T|t}\leftarrow f_{i-1,S,T}\)\(f_{i,S,T}\leftarrow f_{i-1,S,T}\)。时间复杂度 \(O(\sigma_0(L)\cdot 2^{2\omega(L)})\)

现在解决第二个问题。

\(G\not\mid x\),答案为 \(0\)。否则令 \(x\leftarrow \dfrac{x}{G}\)。此时若 \(x\not\mid L\),答案为 \(0\)。考虑此外的情况。

能够想到将前缀和和后缀和合并。时间复杂度 \(O(\sigma_0(L)\cdot 3^{2\omega(L)})\)

发现合并复杂度瓶颈在于枚举子集。使用高维前缀和预处理,合并可以做到 \(O(2^{2\omega(L)})\)

总时间复杂度 \(O(\sigma_0(L)\cdot 2^{2\omega(L)})\)

代码好冗长。


B

问序列 \(\{a_n\}\) 有多少种合法的划分 \(\{S\}\) 满足每个 \(S_i\)\(\min\le \operatorname{len}\le\max\)。答案对 \(10^9+7\) 取模。

\(n\le 5\times 10^5\)\(\mathrm{TL}=0.5\mathrm{s}\)


注意到 \(\min\) 不升,\(\operatorname{len}\) 单增,可以 \(O(n\log n)\) 二分出满足 \(\min\le \operatorname{len}\) 的最小 \(\operatorname{len}\)

有两个暴力策略。枚举所有前缀的后缀 \(\max\),或者枚举所有后缀的前缀 \(\max\)。后者直接放了 \(100\) 分。

正解是 CDQ 分治然后双指针 \(O(n\log n)\)。膜拜 Logic_J_X。


C

给出两个数 \(n,m\)。你需要构造非负整数序列 \(\{a_n\}\)

\(n\)要求形如 \((p_i,b_i,t_i)\):若 \(\displaystyle \sum_{j=i}^{p_i}a_i<b_i\),产生 \(t_i\) 的代价。保证 \(i\le p_i\)

\(m\)限制形如 \((x_i,y_i,c_i)\)\(\{a_n\}\) 必须满足 \(\displaystyle \sum_{j=x_i}^{y_i}a_i\le c_i\)。保证 \([x_i,y_i]\) 是若干 \([i,p_i]\) 的不交并。

求出最小代价。

\(n,m\le 5\times 10^5\)\(b_i,c_i,t_i\le 10^9\)\(\mathrm{TL}=1.5\mathrm{s}\)


\(\{a_n\}\) 作前缀和,要求 \(\displaystyle \sum_{j=i}^{p_i}a_j\ge b_i\) 等价于 \(sum_{i-1}-sum_{p_i}\le -b_i\),用差分约束系统建图 \((p_i,i-1,-b_i)\),得到 \(n+1\) 个点的以 \(n\) 为根的外向树。

限制 \(\displaystyle \sum_{j=x_i}^{y_i}a_j\le c_i\) 等价于 \(sum_{y_i}-sum_{x_i-1}\le c_i\),建边 \((x_i-1,y_i,c_i)\),由于题目给出的 \([x_i,y_i]\) 的性质,建出的边一定连向自己的祖先。

图上的负环一定由一个节点 \(u\) 到其后代 \(v\) 的树边与 \(v\)\(u\) 的非树边组成。称 \(u\)\(v\) 的链为负环链,\(u\) 为上端点,\(v\) 为下端点。

转化为使得每条负环链至少断掉一条边的最小代价。

\(\operatorname{link}(x)\) 为以节点 \(x\) 为下端点的负环链的上端点的最大深度(\(dep_n=1\)),不存在则为 \(0\)

\(w_x\) 为断开 \((fa_x,x)\) 的代价,\(f_{x,i}\) 为两个端点都在 \(x\) 子树内的负环链以及下端点在 \(x\) 子树内且上端点深度大于 \(i\) 的负环链都至少被断开一条边的最小代价。

  • \(\operatorname{link}(x)=0\)

此时有转移方程

\[f_{x,i}=\min\Big(\sum_{y\in{\operatorname{son}(x)}}f_{y,i},w_x+\sum_{y\in{\operatorname{son}(x)}}f_{y,dep_x+1}\Big) \]

  • \(\operatorname{link}(x)>0\)

此时根据 \(i\)\(dep_x\) 的大小关系得到

\[f_{x,i}=\begin{cases}\min\Big(\sum_{y\in{\operatorname{son}(x)}}f_{y,i},w_x+\sum_{y\in{\operatorname{son}(x)}}f_{y,dep_x+1}\Big)&i\ge dep_x\\w_x+\sum_{y\in\operatorname{son}(x)}f_{y,dep_x+1}&i<dep_x\end{cases} \]

使用线段树合并维护。

时间复杂度 \(O(n\log n)\)


D

[ARC080F] Prime Flip

*3078。

有一个无穷长的 01 序列。其中 \(\{x_n\}\) 中的位置是 \(0\),其他均为 \(1\)

每次可以选择一个长为奇素数的连续段翻转。问最后全为 \(1\) 的最少操作次数。

\(1\le n\le 1000\)\(1\le x_1<x_2<\dots<x_n\le 10^7\)。AtCoder 中 \(n\le 100\)


先差分。考虑翻转两个数的代价。

  • 差为奇质数,代价为 \(1\)

  • 差为偶数,由哥德巴赫猜想得到代价为 \(2\)

  • 差为奇数且不为奇质数,可以由偶数和奇质数拼成,代价为 \(3\)

贪心地先选第一种,然后第二种,最后第三种。

第一类由于差为奇质数,边在奇偶之间,二分图匹配可以做到 \(O(n^3)\)\(O(n^{2.5})\)

剩下的情况直接统计。时间复杂度 \(O(n^{2.5})\)

#include<bits/stdc++.h>
#define N 2010
#define M 8000010
#define inf (1<<30)
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int S,T;
int head[N],ver[M],nxt[M],edge[M],tot=1;
void add(int u,int v,int w){
	nxt[++tot]=head[u],ver[tot]=v,edge[tot]=w,head[u]=tot;
	nxt[++tot]=head[v],ver[tot]=u,edge[tot]=0,head[v]=tot;
}
int d[N],now[N],flow,mf;
bool bfs(){
	memset(d,0,sizeof(d));
	queue<int>q;
	q.push(S),d[S]=1,now[S]=head[S];
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head[x],y;i;i=nxt[i]){
			if(edge[i]&&!d[y=ver[i]]){
				now[y]=head[y],d[y]=d[x]+1,q.push(y);
				if(y==T)return true;
			}
		}
	}
	return false;
}
int dinic(int x,int flow){
	if(x==T)return flow;
	int ret=0;
	for(int i=now[x],y;i&&flow;i=nxt[i]){
		now[x]=i,y=ver[i];
		if(edge[i]&&d[y]==d[x]+1){
			int k=dinic(y,min(flow,edge[i]));
			if(!k)d[y]=0;
			edge[i]-=k,edge[i^1]+=k;
			ret+=k,flow-=k;
		}
	}
	return ret;
}
int n,m,a[N],b[N];
bool check(int x){
	if(x<3)return false;
	for(int i=2;i*i<=x;i++)
		if(x%i==0)return false;
	return true;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	b[++m]=a[1],b[++m]=a[n]+1;
	for(int i=2;i<=n;i++)
		if(a[i]-a[i-1]!=1)b[++m]=a[i],b[++m]=a[i-1]+1;
	S=m+1,T=m+2;
	int o=0;
	for(int i=1;i<=m;i++){
		if(b[i]&1)add(S,i,1),o++;
		else add(i,T,1);
	}
	for(int i=1;i<=m;i++)
		for(int j=i+1;j<=m;j++){
			if(check(abs(b[j]-b[i])))
				(b[i]&1)?add(i,j,1):add(j,i,1);
		}
	while(bfs()){
		while(flow=dinic(S,inf))
			mf+=flow;
	}
	int p=m-o-mf;o-=mf;
	int ans=mf+p/2*2+o/2*2+(p&1)*3;
	printf("%d\n",ans);
	
	return 0;
}

其他三个题都不想补。

posted @ 2023-11-14 22:17  SError  阅读(21)  评论(0编辑  收藏  举报