11.4 模擬賽

lucky_king發表於2020-11-04

這分數羞於出口(fuck)

T1 大空魔術

題目:

宇佐見蓮子是一個嚮往太空旅行的大學生。但迫於月面旅行團高額的費用,她暫時放棄了幻想,開

始專心於物理學的研究。

在一次偶然的經歷中,蓮子發現了兩種新的粒子,她將這兩種粒子命名為「粒子 A」和「粒子 B」 。

蓮子發現,兩種粒子在不受外界刺激的情況下可以保持穩態,並且會相互吸引。多個粒子在相互的

吸引力作用下會形成一個有序的序列。

蓮子還發現,當特定種類的兩個粒子以特定方式排列時,給予它們一種特定的刺激,就會發生「互

毀」現象:兩個粒子會發生完全的物質-能量轉換,轉化為大量能量。

經過長時間的研究,蓮子發現了「互毀」現象發生的條件:當前僅當相鄰的兩個粒子呈現:”AB”

或”BB” 的形式時,給予特定刺激後它們才會發生「互毀」 。

現在,蓮子在實驗室中將眾多粒子排成了多個序列,她想通過給予刺激的方式,用這些粒子得到盡

可能多的能量,即留下儘可能少的粒子。

由於粒子會相互吸引,每當相鄰的兩個粒子發生「互毀」後,它們左右兩側的粒子還會拼在一起,

仍然保持一個序列的形態。

但粒子數實在是太多了,於是她找到你,請你幫助她求出最後剩餘粒子數的最小值。

輸入:

第 1 行一個整數 t,代表粒子序列的個數。

第 2∼ t 行,第 i + 1 行包含一個只由字元 'A' 和 'B' 構成的字串 \(s_i\) ,描述了一個粒子序列。

輸出:

一個整數,代表最後剩餘粒子數

樣例:

3

AAA

BABA

AABBBABBBB

3

2

0

解題思路:

對於這個有序序列,我們能刪就刪,儘管有 ABBA這種情況,我們發現會有 兩種刪法,但是它產生的貢獻卻都是一樣的,都是 2,所以我們可以根據這個貪心,列舉子串,### 利用一下棧,然後碰上A就入棧,碰上B就出棧,因為無論什麼情況,造成出棧的一定是有B的子串,所以列舉到最後只需要看一下還剩下多少就OK了,

程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <cstdlib>
#define inf 0x3f
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
std::stack<char> s; 
int main()
{
	int t=read();
	while(t--)
	{
		std::string ch;
		std::cin>>ch;
		for(int i=0;i<=ch.length();i++)
		{
			if(ch[i]=='A')
			{
				s.push(ch[i]);
				continue;
			}
			else if(ch[i]=='B')
			{
				if(s.empty()) 
				{
					s.push(ch[i]);
				}
				else 
				{
					//char top=s.top();
					s.pop();	
				}	
			}
		}
		printf("%d\n",s.size());
	}
	return 0;
}

T2 夜桜街道

題目描述:

深夜,離開實驗室後,蓮子與好友梅麗相約來到了一條長滿櫻花樹的街道,她們決定從街道的左側

某個位置出發向右走,欣賞沿途的每一棵櫻花。

她們發現,這條街道上共有 n 棵櫻花樹,從左到右的編號依次為 1,2,···n,每一棵櫻花樹都有自

己的美麗值,編號為 i 的櫻花樹的美麗值為\(a_i\)

蓮子喜歡驚喜的感覺。若她以某個位置作為起點向右走,她看到一棵櫻花樹的驚喜度定義為:從起

點到這棵樹途經的所有樹中,比這棵樹的美麗值小的個數。

梅麗決定用平均值描述蓮子的平均驚喜度。她定義區間 [l,r] 的平均驚喜度為,從櫻花樹 l 開始走,

向右走到櫻花樹 r 時,看到所有櫻花樹的驚喜度之和,除以區間中櫻花樹的個數。

現在,蓮子和梅麗想知道,以櫻花樹 1 為起點,分別以櫻花樹 1 ∼ n 為終點時,蓮子的平均驚喜

度之和。

由於她倆都不喜歡小數,你只需要輸出這個值對 998244353 取模的結果。

輸入:

第 1 行一個整數 n,表示櫻花樹的棵樹。

第 2 行有 n 個整數,第 i 個整數表示編號為 i 的櫻花樹的美麗值\(a_i\)

輸出:

一行一個整數,表示答案。

樣例:

6

1 1 4 5 1 4

748683269

資料:

對於測試點 1 ∼ 6 ,保證 n ≤ 1000。

對於測試點 1 ∼ 10,保證 n ≤ 5000。

對於測試點 1 ∼ 20,保證 n ≤\(10^6\)

對於所有測試點,保證 n ≤\(10^6\) ,0 ≤\(a_i\)\(2^{30}-1\)

解題思路:

我們輸入每個\(a_i\)的時候我們可以用樹狀陣列去維護一下它前面的比它小的數,怎麼統計,我們就可以考慮權值樹狀陣列,但是資料開不了,所以需要離散化一下,然後在,如果

離散化的話,就求個順序對,然後,再利用逆元,求解就行了。

程式碼:

未進行離散化的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <cstdlib>
#define inf 0x3f
#define lowbit(x) x&(-x)
#define int long long
const int maxn=1e6;
const int mod=998244353;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n; 
int num[maxn],s[maxn],tree[maxn],inv[maxn];
int MAX;
void add(int x,int k)
{
	for(int i=x;i<=MAX;i+=lowbit(i))
	{
		tree[i]++;
	}
}
int find(int x)
{
	int sum=0;
	for(int i=x;i>=1;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		num[i]=read();
		MAX=std::max(MAX,num[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		s[i]=find(num[i]-1);//小於等於的為 find(num[i]), 
		//std::cout<<s[i]<<"TEXT"<<std::endl;
		add(num[i],1);
	}
	int ans=0;
	inv[1]=1;
	/*for(int i=1;i<=n;i++)
	{
		// -k * r_-1
		inv[i]=(-p/i)*inv[p%i];
	} */
	for(int i=2;i<=n;i++)
	{
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=1;i<=n;i++)
	{
		s[i]+=s[i-1];
		s[i]%=mod;
		ans=(ans+inv[i]%mod*s[i])%mod;
		ans%=mod;
//		std::cout<<inv[i]<<std::endl;
//		std::cout<<ans<<std::endl;
	}
	printf("%lld",ans);
	return 0;
}

進行了離散化的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <cstdlib>
#define inf 0x3f
#define lowbit(x) x&(-x)
#define int long long
const int maxn=1e6;
const int mod=998244353;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n; 
int num[maxn],s[maxn],tree[maxn],inv[maxn];
int MAX;
void add(int x,int k)
{
	for(int i=x;i<=MAX;i+=lowbit(i))
	{
		tree[i]++;
	}
}
int find(int x)
{
	int sum=0;
	for(int i=x;i>=1;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}
int b[maxn];//離散化用的陣列 
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		num[i]=read();
		b[i]=num[i];
		MAX=std::max(MAX,num[i]);
	}
	std::sort(b+1,b+n+1);//從小到大排序
	int len = std::unique(b+1,b+n+1)-b-1; 
	for(int i=1;i<=n;i++)
	{
		num[i]=std::lower_bound(b+1,b+len+1,num[i])-b;
		MAX=std::lower_bound(b+1,b+len+1,MAX)-b;
	}
	for (int i = 1; i <= n; i++)
	{
		s[i]=find(num[i]-1);//小於等於的為 find(num[i]), 
		//std::cout<<s[i]<<"TEXT"<<std::endl;
		add(num[i],1);
	}
	int ans=0;
	inv[1]=1;
	/*for(int i=1;i<=n;i++)
	{
		// -k * r_-1
		inv[i]=(-p/i)*inv[p%i];
	} */
	for(int i=2;i<=n;i++)
	{
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=1;i<=n;i++)
	{
		s[i]+=s[i-1];
		s[i]%=mod;
		ans=(ans+inv[i]%mod*s[i])%mod;
		ans%=mod;
//		std::cout<<inv[i]<<std::endl;
//		std::cout<<ans<<std::endl;
	}
	printf("%lld",ans);
	return 0;
}

T3科學世紀

問題描述:

t 組資料,每次給定整數 p,q,求一個最大的整數 x,滿足 p 可以被 x

整除,並且 q 不能整除 x。

輸入:

第 1 行一個整數 t,代表資料組數。

第 2 ∼ t + 1 行,每行兩個整數 p,q,代表給定的兩個引數。

輸出:

共 t 行,每一行代表對應的 x 的值。

樣例:

輸入:

3

10 4

12 6

179 822

輸出:

10

4

179


思路分析:

顯然,如果 \(p<q\),那麼\(p|x\)的時候,\(p\not|x\),很顯然,\(p\)就符合輸出的條件,那麼我們這個時候輸出p 就好了,同時,\(p\) % \(q\) !=0的時候,也就輸出\(p\)就好了,

那現在就是考慮其他情況的時候,我們考慮一下,\(p\)\(q\)的質因子,但是\(p\)中有比\(q\)質因子大的,但它對答案並不產生貢獻,我們做的是列舉刪除某個質因子,然後尋求最大值(為什麼要刪去一個,刪去多個就會使得\(x\)下降,導致不是最優解)

所以解法就是 :列舉 \(q\)的每一個質因子,然後令\(p\)去除以它,直到\(p\)中的質因子的數要小於\(q\)中質因子數,然後比較出最大值來,然後就是答案;

總複雜度 O(\(t\sqrt{q}\)


先對 \(p,q\) 質因數分解,設 \(\{a_i\}\) 為質數集,\(\{b_i\}\) 為對應質數的次數:

$$p=\prod a_{i}^{b1_i}$$

$$q=\prod a_{i}^{b2_i}$$

\((x|p) \land (q\nmid {x})\),則 \(x\) 質因數分解後有:

$$x=\prod a_{i}^{b3_i}$$

$$p=k\times x =k\times \prod a_{i}^{b3_i}(k\in \mathbf{N}^*)$$

$$∃a_j|q,\ b3_j < b2_j$$

第二個條件表示 \(x|p\),第三個條件表示存在一個整除 \(q\) 的質數 \(a_j\),它在 \(x\) 中的次數比在 \(q\) 中的次數要小,從而使 \(q\nmid x\)

顯然,最優的 \(x\) 一定是 \(p\) 摳去一些質因子 \(a_j\),使得該質因子在 \(p\) 中的次數小於 \(q\) 中的次數後剩下的值。

顯然摳去的質因子最多有一個。

所以可以列舉 \(q\) 的所有質因子並進行計算。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define int long long
#define INF (1e13 + 7)
#define MANX MAXN
#define MAXN 2000000

using namespace std;

inline int read()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + (c ^ 48); c = getchar();}
	return f * x;
}

int a[MAXN], b[MAXN], numa, numb;

signed main()
{
	int t = read();

	while (t--)
	{
		int p = read(), q = read();
		
		if (q > p || p % q != 0) {cout << p; puts(""); continue;}//---
		
		int x = p, y = q, ans = 1;//---
		
		for (int i = 2; i <= sqrt(y); i++)
		{
			if (y % i == 0)
			{
				numa = 0, numb = 0;
				while (x % i == 0) x /= i, numa++;
				while (y % i == 0) y /= i, numb++;
				int num = numa - numb + 1, k = p;//p的質數的數量減去q質數的數量 
				for (int j = num; j; j--) k /= i;
				ans = max(ans, k);
			}
		}
		
		if (y != 1)
		{
			numa = 0, numb = 0;
			while (x % y == 0) x /= y, numa++;
			numb++;
			int num = numa - numb + 1, k = p;
			for (int i = num; i; i--) k /= y;
			ans = max(ans, k);
		}

		cout << ans;
		puts("");
	}
	return 0;
}