1. 程式人生 > >2018提高組模擬10(未完)

2018提高組模擬10(未完)

2018提高組模擬10

————————————————————————————————————————20181005

T1

階乘

(WOJ4043)

素數線性篩    分解質因數   數學推理

描述

有n個正整數a[i],設它們乘積為p,你可以給p乘上一個正整數q,使p*q剛好為正整數m的階乘,求m的最小值。

輸入

共兩行。

第一行一個正整數n。

第二行n個正整數a[i]。

輸出

共一行

一個正整數m。

樣例輸入

1 6

樣例輸出

3

提示

樣例解釋:

當p=6,q=1時,p*q=3!

【資料範圍與約定】

對於10%的資料,n<=10

對於30%的資料,n<=1000

對於100%的資料,n<=100000,a[i]<=100000

題解

先分解所有的質因數

題目要求一個最小的m使m!包含 p這個因子。  可以把p分解質因數,假設 p=∏ai^bi(ai為質數),那麼只要 m!包含了 所以對於每個ai^bi,分別求出滿足條件的最小的 m,取最大值即可。  怎麼求m?  先看一個簡單的問題:  27!裡面有多少個3相乘?  27!=1*2*...*27  包含1個3的數有27/(3^1)=9個  包含2個3的數有27/(3^2)=3個  包含3個3的數有27/(3^3)=1個  總共:9+3+1=13 個  所以27!裡面有13個3相乘。  用這個方法就可以求得m!

我們可以各個質因數及它的個數算出一個最小的滿足的m

再取個MAX

但,語言系統太弱了,無法完成口胡,就自行看程式碼吧

另外,還有二分的方法:*(^_^)*

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
#define ll long long
int n,a[100010],b[100010];
int pri[96]={0,2,3,5,7,11,13,17,19,23,
29,31,37,41,43,47,53,59,61,67,
71,73,79,83,89,97,101,103,107,109,
113,127,131,137,139,149,151,157,163,167,
173,179,181,191,193,197,199,211,223,227,
229,233,239,241,251,257,263,269,271,277,
281,283,293,307,311,313,317,331,337,347,
349,353,359,367,373,379,383,389,397,401,
409,419,421,431,433,439,443,449,457,461,
463,467,479,487,491,499};
queue<int>q;
void divide(int x){
	for(int i=1;pri[i]*pri[i]<=x&&i<=95;i++){
		if(x%pri[i]==0){
			q.push(pri[i]);
			do{
				x/=pri[i];
				b[pri[i]]++;
			}while(x%pri[i]==0);
		}
	}
	if(x>1){
		q.push(x);
		b[x]++;
	}
}
int r;
ll ans=0,c;
ll ques(int x){
	int an=0;
	while(b[x]>0){
		an+=(b[x]/(x+1)*(x-1)+b[x]%(x+1));
		b[x]/=(x+1);
	}
	return 1ll*an*x;
}
int main(){
	n=read();
	for(int i=1;i<=n;++i){
		a[i]=read();
		divide(a[i]);
	}
	while(!q.empty()){
		r=q.front();q.pop();
		c=ques(r);
		if(c>ans)ans=c;
	}
	printf("%lld",ans);
	return 0;
}

T2

上升序列

(WOJ4044)

狀壓DP    DFS部分分

描述

給出一個長度為 m 的上升序列 A(1 ≤ A[i]≤ n), 請你求出有多少種 1...n 的排列, 滿足 A 是它的一個 LIS.

輸入

第一行兩個整數 n,m.

接下來一行 m 個整數, 表示 A.

輸出

一行一個整數表示答案.

樣例輸入

【輸入樣例1】 5 3 1 3 4 【輸出樣例1】 11 【輸入樣例2】 4 2 3 4 【輸出樣例2】 5

樣例輸出

提示

【資料範圍與約定】

對於前 30% 的資料, n ≤ 9;

對於前 60% 的資料, n ≤ 12;

對於 100% 的資料, 1 ≤ m ≤ n ≤ 15.

題解

DFS暴利出所有方案,都做一次最長上升子序列的DP,看那些合法。

如果中途已經發現不合法(給出的陣列沒有按規定排序,最長上升子序列大於了給出的),就不往下搜了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
#define ll long long
int n,m,c[20],a[20];
int b[20],f[20],d[20];
int dfs(int now){
	/*for(int i=1;i<=now;i++)
		cout<<i<<" "<<b[i]<<" "<<f[i]<<" "<<d[i]<<endl;
	cout<<endl;*/
	if(now>n){
		/*for(int i=1;i<now;i++)
			cout<<d[i]<<" ";
		cout<<endl;*/
		return 1;
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(b[i]||(a[i]&&!b[c[a[i]-1]]))continue;
		for(int j=0;j<now;j++){
			if(i>d[j])f[now]=max(f[now],f[j]+1);
		}
		if(f[now]>m){
			f[now]=0;
			continue;
		}
		b[i]=1;d[now]=i;
		ans+=dfs(now+1);
		f[now]=0;
		b[i]=0;d[now]=0;
	}
	return ans;
}
void work_1(){
	b[0]=1;
	printf("%d",dfs(1));
}
int main(){
	n=read();m=read();
	for(int i=1;i<=m;i++){
		c[i]=read();
		a[c[i]]=i;
	}
	//if(n<=9&&m<=9)
		work_1();
	return 0;
}

正解…………

T3

(WOJ4045)

LCA    樹上差分    樹狀陣列

相遇

描述

豪哥生活在一個n個點的樹形城市裡面,每一天都要走來走去。雖然走的是比較的多,但是豪哥在這個城市裡面的朋友並不是很多。

當某一天,猴哥給他展現了一下大佬風範之後,豪哥決定要獲得一些交往機會來提升交往能力。豪哥現在已經物色上了一條友,打算和它(豪哥並不讓吃瓜群眾知道性別)交往。豪哥現在spy了一下這個人的所有行程起點和終點,豪哥打算從終點開始走到起點與其相遇。但是豪哥是想找話題的,他想知道以前有多少次行程和此次行程是有交集的,這樣豪哥就可以搭上話了。這個路徑與之前路徑的有交集數量作為豪哥此次的交往機會。

但是豪哥急著要做交往準備,所以算什麼交往機會的小事情就交給你了。

輸入

第一行一個正整數n表示節點個數。接下來n-1行,每行兩個正整數分別是u,v表示節點u和v之間有連邊。接下來一行一個 正整數m表示路徑個數。然後有m行,每行兩個正整數分別是u,v分別表示u到v之間有一條路徑

輸出

輸出共m行,每行一個整數,第i行表示豪哥在這條路徑上獲得的交往機會。

樣例輸入

5 1 2 1 3 3 4 3 5 4 4 5 4 2 1 3 1 2

樣例輸出

0 1 2 2  

提示

【資料範圍與約定】

對於20%的資料n,m≤2000

對於另外20%的資料n,m≤50000

對於另外10%的資料n,m≤200000保證樹形結構是一條鏈

對於另外50%的資料n,m≤200000

題解

已知路徑求樹上所有節點被路徑覆蓋次數:

1.對每條路徑的 起點a和終點b 的權值 +1 , 對 lca(a, b) 的權值 -1 , 對 lca(a, b)的父節點 權值 -1

2.從根節點開始深搜,回溯時將其本身的權值加上所有子節點的權值

3.每個節點的權值既是其被路徑覆蓋的次數

於是可以轉換為兩個問題,用兩個線段樹維護:

1)當前LCA在之前路徑上 ,樹狀陣列區間修改單點查詢

2)當前路徑有之前LCA ,樹狀陣列單點修改區間查詢

…………