1. 程式人生 > >CF Round #520 (Div. 2)

CF Round #520 (Div. 2)

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;
}