CF Round #520 (Div. 2)

版权声明:转载请在原文附上源连接以及作者,谢谢~ https://blog.csdn.net/weixin_39778570/article/details/84113084

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
题目链接:http://codeforces.com/contest/1062
官方题解:http://codeforces.com/blog/JATC

A题

题意:求最长连续子数字串中间有几个数,若该串的一个端点或两个端点为最左端,或最右段时则该端点算作中间的数。
解法:
Since 1≤ai≤10^3 for all i, let set a0=0 and an+1=1001.
For every i,j such that 0≤i<j≤n+1,
if aj-j=ai-i then we can erase all the elements between i and j (not inclusive).
So just check for all the valid pairs and maximize the answer. Time complexity: O(n^2).
虚拟两个左右端点就可以避免特判了

#include<bits/stdc++.h>
#define ll long long 
#define fo(i,j,n) for(int i=j; i<=n; i++) 
using namespace std;
int n,a[1005];
int main(){
	cin>>n;
	fo(i,1,n)scanf("%d",&a[i]); 
	int ans = 0;
	a[n+1]=1001; // 左右端点需要特判,加上虚拟的左右端点 
	fo(i,0,n)fo(j,i+1,n+1){
		if(a[j]-a[i]==j-i){
			ans = max(ans,j-i-1); // 去掉左右端点 
		}
	}
	cout<<ans;
	return 0;
} 

B题

题意:一个数可以有两种操作,乘以某一个数,或者开方。求得到的最小的数是多少,需要多少此操作。
解法:
By factorizing n we get n=p1a1p2a2…pkak (k is the number of prime factors).
Because we can’t get rid of those prime factors then the smallest n is p1p2…pk.
For each ai, let ui be the positive integer so that 2ui≥ai>2ui-1. Let U be max(ui). //2^U才能连续开方
It’s clear that we have to apply the “sqrt” operation at least U times,
since each time we apply it, ai is divided by 2 for all i.
If for all i, ai=2U then the answer is U, obviously.
Otherwise, we need to use the operation “mul” 1 time to make all the ai equal 2U and by now the answer is U+1.
一个数不可再分时,当且仅当一个数没有平方因子,也就是说这个数分解为p1p2p3…等都是1次方的时候无法继续分解。求分解次数,因为是连续开方运算,所以所有ai(注:piai)一定得为2的幂,不为2的幂的话就凑为2的幂(大于等于所有ai且最接近的,记为ans),我们总可以做一次或0次乘法运算使得所有质数因子的幂为ans,那么答案就是ans或ans+1(只要有一个因子不够就得做一次乘法)

#include<bits/stdc++.h>
using namespace std;
int n,k=1,f,ans,mx,num,cnt,a[1150];
int main()
{
	scanf("%d",&n);
	for(int i=2;i*i<=n;i++) // 分解n
	  if(n%i==0)
	    {
	    while(n%i==0)n/=i,num++;
	    mx=max(mx,num);
	    a[++cnt]=num; // 储存指数
	    k*=i,num=0;
		}
	if(n>1)mx=max(mx,1),k*=n,a[++cnt]=1;
	while((1<<ans)<mx)ans++; // 找到最接近的最大因子的指数的一个为2的幂的数
	for(int i=1;i<=cnt;i++)if(a[i]<(1<<ans)){f=1;break;} // 是否需要乘法
	printf("%d %d\n",k,ans+f);
	return 0;
}

C题

题意:看题吧…
解法:设区间[l,r]中能有k个1,x个0,先加1后加0,因为先加最大的数后加小的数,因为同一次操作大的数对其他数产生的贡献多于小的数
1的贡献为 1,2,4,8,16…2^(k-1) = (1<<k)-1
0的贡献为 2k-1, 2(k+1)-2, 2(k+2)-4, 2(k+3)-8
加起来为 1+2+4+8+16+…+2(k-1)+2k+2(k+1)+…+2(n-1) - 1-2-4-8-…-2(x-1) = 2n-1 - (2x-1) = 2n-2x

#include<bits/stdc++.h>
#define ll long long 
#define fo(i,j,n) for(int i=j; i<=n; i++) 
using namespace std;
const int maxn = 100005, mod = 1e9+7;	
ll q_mod(ll a, ll n, ll mod){ // mod很大要用ll 类型 , 切记
	ll ret = 1;
	a %= mod;
	while(n){
		if(n&1) ret = ret*a%mod;
		a = a*a%mod;
		n>>=1;
	}
	return ret%mod; // 注意结果也要,可能有0次方的情况 
}
int n,q,sum[maxn],num[maxn];
char s[maxn];
int main(){
	cin>>n>>q;
	scanf("%s",s+1);
	fo(i,1,n){
	//	if(s[i]==0)bit.add(i,1);
		if(s[i]=='0') sum[i] = sum[i-1]+1;
		else sum[i] = sum[i-1];
	}
	num[0] = 1;
	fo(i,1,n) num[i] = (num[i-1]<<1)%mod; 
	while(q--){
		int l,r;
		scanf("%d%d",&l,&r);
		int x = sum[r] - sum[l-1];
		int ans = ((num[r-l+1] - num[x])%mod+ mod)%mod;  
	//	int ans = ((q_mod(2,r-l+1,mod) - q_mod(2,x,mod))%mod+ mod)%mod;	//也可以
		printf("%d\n",ans);
	}
	return 0;
}

D题

解法 一
在 2-n中
a,b能互相转换,当且仅当一个是另一个的2倍或更多倍数
b>a 时 a->b或b->a 转换的贡献为 b/a
val[a] 表示 所有能转化为a点的贡献
对每一个a,b建立无向边
但实际上对于一对a,b来说,都可以 a -> b -> -a -> -b -> a 都可以回到原点,并且每个点能产生4次贡献
所以在进行一遍搜索的时候,只计算了两个贡献,搜索出最大的转移链,最后结果要乘2
解法二:
for i : 2-n : ans += i * (n / i - 1); ans = 4;
(n / i - 1) 表示 2
i 到 n 之间的 i 的倍数的个数
为什么对每个i都可以加起来呢?因为从一开始2的倍数是所有偶数,奇数x的倍数为2*x必然可以由2转移过去,
所以对于,每个(n / i - 1)的i都是可达的。
法一:

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1e5+5;
int n;
vector<int> G[maxn];
bool vis[maxn];
ll val[maxn];
ll dfs(int u){
	ll ret = val[u]; // 到达这个点,加上到达这个点的贡献 
	vis[u] = 1;	
	for(int v:G[u])if(!vis[v])ret += dfs(v);
	return ret;
}
int main(){
	cin>>n;
	fo(i,2,n)for(int j=i+i; j<=n; j+=i){
		val[i] += j/i; // i 这个点的总贡献, 能到达的话也意味着i能反向到达,所以不用真的去计算有哪些j到i,在到达i这个点时把能到达i的贡献加上就行 
		val[j] += j/i; // 同理,i访问到了j,就加上能到达j的所有i的贡献 
		G[i].push_back(j);
		G[j].push_back(i);
	}
	ll mx = 0;
	fo(i,2,n)if(!vis[i])mx = max(mx,dfs(i));
	printf("%lld\n",2ll*mx);
	return 0;
} 

法二:

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n;
int main(){
	cin>>n;
	ll ans = 0;
	fo(i,2,n) ans += i*(n/i-1);
	cout<<ans*4;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39778570/article/details/84113084