2019 藍橋杯國賽 B 組模擬賽 題解


標簽 ok

#include<bits/stdc++.h>
using namespace std;

/*
求階乘 去除尾部0
每次求階乘時:結果去除尾0,並對 1e6取余
*/

typedef long long ll;

ll n = 1325476;
const ll mod = 1e6;

ll ans = 1;

void solve(){
	while(ans%10 == 0) ans = ans/10;
	ans = ans%mod;
}

int main(){
	for(ll i = n;i>=1;i--){
		ans = ans * i;
		solve();
	}
	printf("%lld",ans);
	return 0;
}
//137664
#include<bits/stdc++.h>
using namespace std;

/*
2 * 5會產生0
做法:每次循環求階乘時 統計2和5個數 並去除2和5,最后乘回來即可 
*/

typedef long long ll;

ll n = 1325476;
const ll mod = 1e6;

ll ans = 1;
ll cnt1 = 0;
ll cnt2 = 0;
int main(){
	for(ll i = 1;i <= n; i++){
		ll x = i;
		while(x%2==0) cnt1++,x=x/2;
		while(x%5==0) cnt2++,x=x/5;
		ans = ans * x % mod;
	}
	if(cnt1 - cnt2 > 0){
		for(int i=1;i<=cnt1-cnt2;i++) ans = ans * 2 % mod;
	}else{
		for(int i=1;i<=cnt2-cnt1;i++) ans = ans * 5 % mod;
	}
	printf("%lld\n",ans);
	return 0;
}
//137664

101串 ok

dfs搜索方法

#include<bits/stdc++.h>
using namespace std;

int num[35];
long long ans = 0;

/*
dfs枚舉2^30種可能結果
運行100秒左右
*/

void dfs(int k){
	if(k==31){
		for(int i=1;i<=28;i++){
			if(num[i] == 1 && num[i+1] == 0 && num[i+2] == 1){
				ans++;
				break;
			}
		}
		return;
	}
	
	num[k] = 1;
	dfs(k+1);
	num[k] = 0;
	dfs(k+1);
}


int main(){
	dfs(1);
	printf("%lld",ans);
	return 0;
}
//1046810092

二進制枚舉方法

#include<bits/stdc++.h>
using namespace std;

/*
二進制枚舉
1<<30種 也就是2^30可能情況
對於每個情況 枚舉每一位 判斷是否滿足條件 
*/

int ans = 0;

int main(){
	for(int i=0;i<(1<<30);i++){
		int x = i;
		int a = 0 ,b = 0, c = 0;
		bool check = false;
		while(x){
			c = b;
			b = a;
			a = x & 1;
			if(a && !b && c){
				check = true;
				break;
			}
			x >>= 1;
		}
		if(check) ans++;
	} 
	cout<<ans<<endl;
	return 0;
} 
//1046810092 

游戲 ok


#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
LL a = 482333897982347239LL;
LL b = 557432748293424892LL;
LL k = 1389472389742429877LL;

//同余 (b-a) % mod = b*2 % mod 
void test(){
	LL mod = a + b;
	cout<<(b-a) % mod<<endl;
	cout<<b*2%mod<<endl;
} 

//快速乘:加法代替乘法 
LL mmul(LL x,LL y,LL p){
	LL ans = 0LL;
	while(y){
		if( y & 1LL) ans = (ans + x) % p;
		x = (x + x) % p;
		y >>= 1;
	}
	return ans;
}

//快速冪 
LL mpow(LL x,LL y,LL p){
	LL ans = 1LL;
	while(y){
		if(y & 1LL) ans = mmul(ans,x,p);
		x = mmul(x,x,p);
		y >>= 1;
	}
	return ans;
}

int main(){
	LL mod = a + b; //首先 c + d = 2*a + b - a  =  a + b = mod
	LL ans = mmul(a,mpow(2LL,k,mod),mod); //d = b - a = b - (mod - b) = 2*b - mod = 2*b %mod,所以 b-a 也就等價於 b*2,等價后a與b交不交換都無所謂(因為后面的操作都是*2)
	printf("%lld\n",min(ans,mod-ans));//結果確保A更小,A可能是ans 也可能是mod-ans(b)
	return 0;
} 
//383513242709218605

公約數 ok

蒜頭君有n個數,他想要從中選出k個數,使得它們的最大公約數最大。
請你求出這個最大的最大公約數。

輸入格式
第一行輸入兩個整數 。
第二行輸入 個整數 。

輸出格式
輸出一個整數。

數據范圍

樣例輸入1
4 3
2 4 8 3
樣例輸出1
2

樣例輸入2
4 2
4 8 6 6
樣例輸出1
6

思路:
30% 暴力dfs 或者 狀態壓縮(二進制枚舉)選出k個數
另外30% 由概率,直接輸出 1。
100% 枚舉可能的gcd 值,檢查有多少元素被它整除即可。時間復雜度O(nlogn)

30%代碼-dfs搜索

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
ll ans = 1;
ll gcd(ll a,ll b){
	if(b==0)
		return a;
	return gcd(b,a%b);
}

const int maxn = 1e6+10;
int n,k;
int a[maxn];

/*
dfs暴搜過30%數據 
*/

/*
不確定的dp狀態轉移方程:dp[i][k] = max(dp[i][k],gcd(dp[j<i][k-1], a[i])
*/ 

void dfs(int cur,int have,ll _gcd){
	
	if(cur == n+1 && have == k+1){
		ans = max(ans,_gcd);
		return;
	}
	if(cur > n+1) return;
	if(have > k+1) return;
	if(have == 1){
		dfs(cur+1,have,_gcd);
		dfs(cur+1,have+1,a[cur]);
	}else{
		dfs(cur+1,have,_gcd);
		dfs(cur+1,have+1,gcd(a[cur],_gcd));
	}
	
}

int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	dfs(1,1,0);
	printf("%lld\n",ans);
	return 0;
}

100%代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e6 + 7;
const int mod = 1e9 + 7;
int a[N+10];

int main(){
	int n,k,x;
	memset(a,0,sizeof(a));
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++){
		scanf("%d",&x);
		a[x]++;//x的個數+1 
	}
	int ans = 1;
	//枚舉gcd可能出現的值:從i=2 ~ N; 
	for(int i = 2 ;i < N; i++){
		int cnt = 0;
		for(int j = i;j < N; j += i) cnt += a[j]; //能被i整除的數 一定是 i i+i i+i+i .... 
		if(cnt >= k) ans = i;//比k大 那么選出k個數 他們的gcd就是當前的i 
	}
	printf("%d\n",ans);
	return 0;
} 

蒜頭圖 ok


實際上就是問圖里有多少個環,計環的個數為 k,則結果為2^k-1。
30% 狀態壓縮選出邊,判斷所選的邊是否構成環
100% 並查集統計環的數量,使用並查集每次詢問只需要判斷這兩點之前是否連通就可以了,為什么結果是2^k-1呢(k表示環的數量),有大佬說了:題目表明了“只要構成蒜頭圖就算一種情況”,那么也就是說從原圖中取1個環也能構成環,取其中兩個環也能構成環(有環就代表是蒜頭圖的子圖,管你取幾個環都是蒜頭圖).......C(n,i) i從1~n都能構成環 那么C(n,1) + C(n,2) + C(n,3) + C(n,n)就是答案,這個式子的值也等於2^n-1,因為C(n,0) + C(n,1) + C(n,2) + C(n,3) + C(n,n) 的結果等於2^n
100%代碼-並查集統計環的數量

#include<bits/stdc++.h>
using namespace std;

const int mod = 1046513837;
int n,m,f[200500];

int find(int x){
	return f[x] == x ? x : f[x] = find(f[x]);
}

void merge(int a,int b){
	int x = find(a);
	int y = find(b);
	if(x != y ) f[x] = y;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) f[i] = i;
	long long ans = 1;
	for(int i=1 ;i<=m;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		int x = find(a), y = find(b);
		if(x == y){//這里如果 兩個點祖先一樣 說明找到環了 
			ans <<= 1; 
			if(ans > mod) ans -= mod;
		}else merge(a,b);
		printf("%lld\n",ans-1);
	}
	return 0;
}

區間並 60% 線段樹做法不會


30% 按照題意暴力計算,枚舉權值區間,枚舉給定的數組的區間 ok

60% 枚舉所有的答案區間l~r,看看哪些區間能恰好分成兩個不相交的區間,暴力做的話:枚舉l和r共O(n2),加上判斷是否恰好分成了兩個區間共O(n3)。判斷是否分成了兩個區間可以用並查集來維護,查詢復雜度可以降到O(1)。
具體怎么用並查集維護?沒寫代碼,但我是這樣想的,在原序列中 如果 a[i] 和 a[i-1] 是連續的 即(后面這個數比前面這個數值大1) 就把當前第i個數 加入第i-1的集合中,代表他們是一個區間的。后面查詢是否分成兩個區間時,只需查詢l~r區間內的數被分成了幾個集合就可以了。
這個思路我自認為沒有問題emmmm

100% 線段樹維護區間最大值、次大值、相應個數 O(nlogn) 不會

30%代碼-暴力枚舉:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 3*1e6+5;
int n;
int m[maxn];
int vis[maxn];
int ans = 0;

/*
枚舉l~r區間
枚舉i~j區間
*/

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&m[i]);
	for(int l=1;l<=n;l++){
		for(int r=l+1;r<=n;r++){
			bool flag = false;
			for(int a=1;a<=n;a++){
				for(int b=a;b<=n;b++){
					for(int c=b+1;c<=n;c++){
						for(int d=c;d<=n;d++){
							bool flag2 = true;
							for(int i=1;i<=n;i++) vis[i] = 0;
							for(int i=a;i<=b;i++) vis[m[i]] = 1;
							for(int i=c;i<=d;i++) vis[m[i]] = 1;
							for(int i=l;i<=r;i++){
								if(vis[i] == 0){
									flag2 = false;
									break;
								}
							}
							if(flag2 && r-l+1 == b-a+1 + d-c+1){
								ans++;
								flag = true;
								//cout<<"l = "<<l<<" r = "<<r<<"a = "<<a<<" b = "<<b<<" c = "<<c<<" d = "<<d<<endl;
								break;
							}
							if(flag) break;
						}
						if(flag) break;
					}
					if(flag) break;
				}
				if(flag) break;
			}
//			if(flag) continue;
		}
	}
	cout<<ans<<endl;
	return 0;
}
/*
5
5 4 3 1 2
*/

100%代碼-線段樹

#include<cstdio>
#include<algorithm>
#define mid (l+r)/2
#define lt (s<<1)
#define rt (s<<1|1)
#define Mn 300005
using namespace std;
long long ans;
int fc[Mn*4],fm[Mn*4],sm[Mn*4],sc[Mn*4];
int p[Mn],nt[Mn],add[Mn*4],n,i,k1,k2,x;

void updata(int s)
{
	int k1=lt,k2=rt;
	if (fm[k1]>fm[k2]) swap(k1,k2);
	if (fm[k1]<fm[k2])
		{
			fm[s]=fm[k1],fc[s]=fc[k1];
			if (sm[k1]<fm[k2])
				sm[s]=sm[k1],sc[s]=sc[k1];
			else
				if (sm[k1]==fm[k2])
					sm[s]=sm[k1],sc[s]=sc[k1]+fc[k2];
				else sm[s]=fm[k2],sc[s]=fc[k2];
		}
	if (fm[k1]==fm[k2])
		{
			fm[s]=fm[k1],fc[s]=fc[k1]+fc[k2];
			if (sm[k1]<sm[k2])
				sm[s]=sm[k1],sc[s]=sc[k1];
			else
				if (sm[k2]<sm[k1])
					sm[s]=sm[k2],sc[s]=sc[k2];
				else sm[s]=sm[k2],sc[s]=sc[k1]+sc[k2];
		}
}
void pplus(int s,int up)
{
	fm[s]+=up; sm[s]+=up;
	add[s]+=up;
}
void push(int s)
{
	if (add[s]!=0)
		pplus(lt,add[s]),pplus(rt,add[s]),add[s]=0;
}
void work(int s,int l,int r,int ls,int rs,int up)
{
	if (l>rs || r<ls) return;
	if (ls<=l && r<=rs)
		return(void)(pplus(s,up));
	push(s);
	work(lt,l,mid,ls,rs,up);
	work(rt,mid+1,r,ls,rs,up);
	updata(s);
}
void build(int s,int l,int r)
{
	if (l==r) return(void)(fc[s]=1,sm[s]=1);
	build(lt,l,mid); build(rt,mid+1,r);
	updata(s);
}
void upans(int s,int l,int r,int ls,int rs)
{
	if (ls<=l && r<=rs)
		{
			if (fm[s]==1 || fm[s]==2) ans+=fc[s];
			if (sm[s]==1 || sm[s]==2) ans+=sc[s];
			return;
		}
	if (ls>r || l>rs) return;
	push(s);
	upans(lt,l,mid,ls,rs);
	upans(rt,mid+1,r,ls,rs);
	updata(s);
}
int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%d",&p[i]),nt[p[i]]=i;
	build(1,1,n);
	for(i=n;i>=1;i--)
		{
			x=nt[i];
			if (p[x+1]<i && p[x-1]<i) work(1,1,n,i,n,1);
			if (p[x+1]<i && p[x-1]>i) work(1,1,n,i,p[x-1]-1,1);
			if (p[x+1]>i && p[x-1]<i) work(1,1,n,i,p[x+1]-1,1);
			if (p[x+1]>i && p[x-1]>i)
				{
					k1=p[x+1]; k2=p[x-1];
					if (k1>k2) swap(k1,k2);
					work(1,1,n,i,k1-1,1);
					work(1,1,n,k2,n,-1);
				}
			upans(1,1,n,i,n);
		}
	printf("%lld",ans-n);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM