Loading

Codeforces 1554 / Codeforces Round #735

Codeforces Round #735 (Div. 2)

Problem A Cherry

题意

给定长度为 \(n\) 的数列 \(a\),求出一个区间,使得这个区间的最大值和最小值的乘积最大。

多组数据,数据组数 \(T \leq 10^4,2 \leq n \leq 10^5,1 \leq a_i \leq 10^6,\sum n \leq 3 \times 10^5\)

题解

显然,长度大于等于 \(3\) 的区间是没用的。

Problem B Cobb

题意

给定长度为 \(n\) 的数列 \(a\),求出 \(\max\limits_{1 \leq i< j \leq n}\{ij - k\cdot(a_i | a_j)\}\),其中 \(\mid\) 是按位或。

多组数据,数据组数 \(T \leq 10^4,2 \leq n \leq 10^5,1 \leq k \leq \min\{n,100\},0 \leq a_i \leq n,\sum n \leq 3 \times 10^5\)

题解

有一个显然错误的贪心思路:选 \(i=n-1,j=n\)。很容易发现这是错的,因为可能存在一组 \(i=i',j=j'\),它们的乘积 \(n(n-1)\) 小,但式子的第二项 \(k\cdot(a_i | a_j)\) 比较 \(i=n-1,j=n\) 时小,它们也有可能成为答案。

注意到 \(a_i|a_j\) 是与 \(n\) 同阶(但不一定小于等于 \(n\),例如 \(n=8,(0111)_2|(1000)_2 = (1111)_2 = (15)_{10}>n\))。

于是式子的第二项与 \(kn\) 同阶。所以,如果 \(i'j'\)\(n(n-1) - k \cdot (a_{n-1}|a_{n})\) 还要小,那么也有 \(i'j'-k \cdot (a_{i'}|a_{j'}) < n(n-1) - k \cdot (a_{n-1}|a_{n})\),则 \(i=i',j=j'\) 一定不会成为答案。当 \(i'\) 不变时,更小的 \(j'\) 也不会成为答案。

倒序枚举 \(i'\)\(j'\),当上述情况出现时就停止枚举。可以证明,该做法的时间复杂度是正确的。事实上,它在大多数测试数据上都表现良好。

# include <bits/stdc++.h>
# define int long long
 
const int N=100010,INF=0x3f3f3f3f;
int a[N];
int n,k;
inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
 
signed main(void){
	int T=read();
	while(T--){
		n=read(),k=read();
		for(int i=1;i<=n;++i){
			a[i]=read();
		}
		int maxx=n*(n-1),ans=maxx-k*(a[n]|a[n-1]);
		for(int i=n;i;--i){
			for(int j=i-1;j;--j){
				if(i*j<=maxx-k*(a[n]|a[n-1])){
					break;
				}
				ans=std::max(ans,i*j-k*(a[i]|a[j]));
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

Problem C Mikasa

题意

给定 \(n,m\),求出最小的不在序列 \(n \oplus 0,n\oplus 1,\cdots,n \oplus m\) 的自然数,即该序列的 \(\operatorname{MEX}\),其中 \(\oplus\) 表示按位异或。

多组数据,数据组数 \(T \leq 3 \times 10^4\)\(0 \leq n,m \leq 10^9\)

题解

注意到对于任何 \(a\),有且仅有一个 \(b\) 满足 \(n \oplus b = a\)。同时,\(n \oplus b = a \Leftrightarrow n \oplus a = b\)。因此,只需要求出一个 \(a\) 使得 \(n \oplus a = b> m\),此时 \(a\) 就是 \(\operatorname{MEX}\)

我们发现 \(>\) 这个限制不太好做,于是转化一下,变成 \(n \oplus a \ge m+1\)。从高到低考虑 \(n\)\(m+1\) 的每一位:

  • 如果 \(n\) 的这一位是 \(0\)\(m+1\) 的这一位也是 \(0\)\(a\)\(0\) 最优。
  • 如果 \(n\) 的这一位是 \(1\)\(m+1\) 的这一位是 \(0\),那么 \(a\)\(0\) 最优。此时一定有 \(n \oplus a \ge m+1\),不需要再考虑接下来的位。
  • 如果 \(n\) 的这一位是 \(0\)\(m+1\) 的这一位是 \(1\),那么 \(a\) 必须取 \(1\)
  • 如果 \(n\) 的这一位是 \(1\)\(m+1\) 的这一位是 \(1\),那么 \(a\) 必须取 \(0\)
# include <bits/stdc++.h>
# define int long long
 
const int N=100010,INF=0x3f3f3f3f;
 
int n,m;
int a[50],b[50];
 
inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
inline int lowbit(int x){
	return x&(-x);
}
signed main(void){
	int T=read();
	while(T--){
		n=read(),m=read();
		if(n>m){
			puts("0");
			continue;
		}
		++m;
		for(int i=35;i>=0;--i){
			a[i]=(((1ll<<i)&n)>0);
			b[i]=(((1ll<<i)&m)>0);
		}
		int ans=0;
		for(int i=35;i>=0;--i){
			if(a[i]==b[i]){
				continue;
			}
			if(a[i]==1&&b[i]==0){
				break;
			}
			if(a[i]==0&&b[i]==1){
				ans|=(1ll<<i);
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

Problem D Diane

题意

构造一个长度为 \(n\) 的由小写字母构成的字符串,使得每个子串的出现次数位为奇数。

多组数据,数据组数 \(T \le 500\)\(1 \leq n \leq 10^5,\sum n \leq 3 \times 10^5\)

题解

\(n\) 为偶数时,构造形如 \(\texttt {aaaa...b...aaa}\),即 \(\dfrac n2\)\(\texttt a\),一个 \(\texttt b\),和 \(\dfrac n2 -1\)\(\texttt a\)

\(n\) 为奇数时,在前面加上一个 \(\texttt c\),就成为了 \(n\) 为偶数的情况。注意判断 \(n=1\)

# include <bits/stdc++.h>
 
const int N=100010,INF=0x3f3f3f3f;
 
inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
 
int main(void){
	int T=read();
	while(T--){
		int n=read();
		if(n%2){
			if(n==1){
				puts("a");
				continue;
			}
			putchar('b');
			for(int i=1;i<=n/2;++i){
				putchar('a');
			}
			putchar('c');
			for(int i=1;i<=n/2-1;++i){
				putchar('a');
			}
			puts("");
		}else{
			for(int i=1;i<=n/2;++i){
				putchar('a');
			}
			putchar('b');
			for(int i=1;i<=n/2-1;++i){
				putchar('a');
			}
			puts("");
		}
	}
	return 0;
}

Problem E You

题意

给定一棵 \(n\) 个节点的树。

重复以下步骤 \(n\) 次,可以得到一个长度为 \(n\) 的序列 \(a\)

  • 每次选取一个没有被删除的节点 \(u\)
  • 计算与它相连的还没有被删除的点的个数(不包括 \(u\) 本身),记为 \(s\)。然后,将 \(a_u\) 赋值为 \(s\)
  • 删除它以及与它相连的边。

对于 \(k=1\cdots n\),计算序列 \(a\) 的数量使得 \(\gcd(a_1,a_2,\cdots,a_n)=k\)。值得一提的是,\(\gcd(x,0)(x \neq 0) =x\)

多组数据,数据组数 \(T \leq 10^4,2 \leq n \leq 10^5, \sum n \leq 3 \times 10^5\)

题解

注意到一条边的 \((u,v)\) 的两个端点删除的先后顺序会影响 \(a_u\)\(a_v\) 的大小。如果 \(u\) 先被删除,那么 \(a_u\) 会因为这条边而增加 \(1\),反之亦然。对于每一条边 \((u,v)\),需要在 \(u\)\(v\) 当中选恰好一个,并将选中的端点的 \(a\) 加上 \(1\)。因此,一共有 \(2^{n-1}\)\(a\) 序列。

我们先来考虑 \(k>1\) 的情况。此时,\(a\) 序列中不应该有 \(i\) 使得 \(a_i = 1\)。不妨设 \(1\) 为这棵树的根。那么,与叶子节点相连的所有边都只能选择给它们父亲的 \(a\) 值加上 \(1\)

接下来,我们来考虑连向子节点的边的选择均已确定的点 \(i\)。如果 \(k \mid a_i\),那么它连向父亲的边只能选择给它父亲的 \(a\) 值加上 \(1\)。否则只能选择给 \(a_i\) 加上 \(1\)。特殊地,如果加上 \(1\) 之后 \(a_i\) 仍然不能被 \(k\) 整除,那么这样的 \(a\) 序列不存在。

于是当 \(k>1\) 时至多存在一个合法的 \(a\) 序列。

\(k=1\) 时的答案即为 \(2^{n-1}\) 减去所有 \(k>1\) 的答案之和。

现在来计算时间复杂度。看上去是 \(O(n^2)\) 的。注意到,对于每一条边 \((u,v)\),都会对 \(a\) 序列的和造成恰好 \(1\) 的贡献。因此,如果 \(k \nmid n-1\),那么合法的序列数量一定为 \(0\)。另外,容易发现在 \(k>1\) 时算法只检验了是否存在一个 \(\gcd\)\(k\) \(k\) 的倍数的序列,而不是恰好\(k\) 的序列。因此,还需要减去 \(2k,3k,...\) 的答案之和,才是真正的答案。

# include <bits/stdc++.h>

const int N=100010,INF=0x3f3f3f3f,MOD=998244353;
typedef long long ll;
struct Edge{
	int to,next;
}edge[N<<1];
int head[N],sum;
int n;
int ans[N],now[N];
inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f; 
}
inline void add(int x,int y){
	edge[++sum]=(Edge){y,head[x]},head[x]=sum;
	return;
}
bool dfs(int i,int fa,int k){
	now[i]=0;
	for(int j=head[i];j;j=edge[j].next){
		int to=edge[j].to;
		if(to==fa)
			continue;
		if(!dfs(to,i,k))
			return false;
	}
	if(now[i]%k==0)
		++now[fa];
	else
		++now[i];
	if(now[i]%k)
		return false;
	return true;
}
inline ll qpow(ll d,ll p){
	ll res=1;
	while(p){
		if(p&1ll)
			res=res*d%(ll)MOD;
		d=d*d%(ll)MOD,p>>=1ll;
	}
	return res;
}
int main(void){
	int T=read();
	while(T--){
		n=read();
		sum=0,std::fill(head+1,head+1+n,0);
		for(int i=1;i<n;++i){
			ans[i]=0;
			int u=read(),v=read();
			add(u,v),add(v,u); 
		}
		ans[n]=0;
		for(int i=2;i<n;++i)
			if((n-1)%i==0)
				ans[i]=dfs(1,0,i);
		for(int i=n-1;i;--i){
			for(int j=2;i*j<n;++j)
				ans[i]-=ans[i*j];
		}
		ans[1]=qpow(2,n-1);
		for(int i=2;i<n;++i){
			ans[1]=(ans[1]-ans[i]+MOD)%MOD;
		}
		for(int i=1;i<=n;++i)
			printf("%d ",ans[i]);
		puts("");
	}
	return 0;
} 
posted @ 2021-09-06 11:54  Meatherm  阅读(61)  评论(0编辑  收藏  举报