zoukankan      html  css  js  c++  java
  • Codeforces 1554 / Codeforces Round #735

    Problem A

    题意

    给定长度为 \(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

    题意

    给定长度为 \(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

    题意

    给定 \(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

    题意

    构造一个长度为 \(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

    题意

    给定一棵 \(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;
    } 
    
  • 相关阅读:
    Js变量类型
    前端面试题HTML
    AP聚类
    锚点
    html
    Active Learning主动学习
    z-index
    position
    学习笔记_卷积神经网络
    学习笔记_深度学习的数学基础
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/15233145.html
Copyright © 2011-2022 走看看