洛谷 2 月月賽 II & EE Round 2

洛谷 2 月月賽 II & EE Round 2

A 出言不遜 題目網址

題目描述

珂愛想出公開賽,但每次都被拒絕。
珂愛很生氣,於是學會了出言不遜。
珂愛用一個字符串 S 存儲了她想說的話,但這句話太遜了。爲了出言不遜,珂愛要對字符串進行操作。每次操作,珂愛可以選擇一個字符 c,若 c 在字符串 S 中出現了
x 次,則珂愛會將 x 個字符 c 補到 S 的尾部。
珂愛認爲,這個字符串長度至少爲 L 時,她才能出言不遜。珂愛想要知道,她至少需要操作多少次,才能讓這個字符串的長度大於等於 L。
如果你不告訴珂愛,珂愛會對你出言不遜。

題目思路

純模擬, 把當前出現次數最多的字母添到字符串尾部, 在判斷就可以了。
注意統計完次數後可以用大根堆來取。
注意特判:如果最長的字母都是0的話, 就沒必要繼續了。

代碼

string s;
unsigned long long L;
map<char, unsigned long long> mp;
priority_queue<unsigned long long>pq;
unsigned long long l;
int main()
{
	cin >> s;
	cin >> L;
	l = s.size();
	if(s.size() == L-1)
	{
		cout << 1 << endl;
		return 0;
	}
	for(int i = 0; i < s.size(); i++)
	{
		mp[s[i]]++;
	}
	for(int i = '0'; i <= '9'; i++)
	{
		pq.push(mp[i]);
	}
	for(int i = 'A'; i <= 'z'; i++)
	{
		pq.push(mp[i]);
	}
	int ans = 0;
	while(l < L)
	{	
		ans++;
		unsigned long long maxn = pq.top();
		pq.pop();
		if(l + maxn < l) break;
		l += maxn;
		maxn *= 2;
		pq.push(maxn);
	}
	cout << ans << endl;
	return 0;
}

B 諤運算 題目網址

題目描述

首先,CYJian 寫出了一個長度爲 n 的數列 a。
然後他靈光一動,寫出了下面這個諤諤的式子:
i=1nj=1nk=1nl=1n(ai or aj) xor (ak ans al)\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^n\sum_{l=1}^n (a_i\ or\ a_j)\ xor\ (a_k\ ans\ a_l)
CYJian 覺得這個是一個諤運算的簡單式子,摁計算器花了 114514s就算出來了答案。
爲了證明你吊打 114514 個 CYJian,請你在
1s 內算出來這個式子的值吧。你只需要給出答案對 232取模的值即可。

題目思路

暴力肯定是不行的,於是我們選擇先進行二進制拆分, 發現其實每一位對他都有貢獻, 我們把四個數都進行二進制拆分, 發現每一位都有貢獻, 我們可以看到爲1就有貢獻, 四個數式子唯一的情況共有10種。
如下:

aia_i 0 1 1 0
aja_j 1 0 1 0
aka_k 0 1 0 0 1 0 0 1 0 1
ala_l 1 0 0 1 0 0 1 0 0 1

記住每位及它後面的的位都有兩種情況。 所以第p位對答案的貢獻還要乘上2p-1;
設n個數中位數爲0的有x個, 1的有y個
則可得方案數爲
2x3y+6x2y2+2xy3=f[p]2x^3y + 6x^2y^2 + 2 xy^3 = f[p]
可能還不太懂, 解釋一下:
第一項:三個0, 一個1, 所以字母爲x3yx^3y, 參考上面的表格, 一共有兩種情況, 所以係數爲二;
以此類推:
最後乘上貢獻在加起來。
最終答案就爲:i=133f[i]2i1\sum_{i=1}^{33} f[i] * 2^{i-1}
別忘取模QAQ~~

代碼

typedef long long ll;
using namespace std;
ll p = 1;
ll n;
ll a[500010];
ll x[500010], y[500010];
ll mi[50];
ll ans;
void cnt(ll k)  // 計算每一位x與y的個數
{
     for(register int i=1;i<=33;i++)
     {
         if(k%2==0) x[i]+=1;
         else y[i]+=1;
         k/=2;
     }
 }
ll f(int i)   // 求f[i]
{
     ll sum=0;
     sum=(((2*x[i]*x[i])%p)*((x[i]*y[i])%p))%p;
     sum=(sum+(((6*x[i]*x[i])%p)*((y[i]*y[i])%p))%p)%p;
     sum=(sum+(((2*x[i]*y[i])%p)*((y[i]*y[i])%p))%p)%p;
     return sum;
}
int main()
{
	cin >> n;
	
	for(int i = 1; i <= 32; i++)
	{
		mi[i] = p;
		p *= 2;
	}
	a[33] = p;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		cnt(a[i]);
	}
	for(int i = 1; i <= 33; i++)
	{
		if(y[i] && x[i]) 
		ans = (ans + (f(i) * mi[i])) % p;
	}
	cout << ans << endl;
	return 0;
}

C 自然溢出啥事兒沒有 題目網址

題目描述

給定一個整數n, 問有多少種長度爲n的字符串, 滿足這個字符串是一個程序片段。
具體定義如下:
單個分號*;* 是一個語句1
空串* * 是一個程序片段[^2]
如果字符串A程序片段, 字符串B語句, 則AB程序片段2
如果字符串A程序片段, 則*{A}語句塊3
如果字符串
A語句塊, 則A語句[]AA都是函數4
如果字符串
A函數, 則(A)是函數, AA()都是5
如果字符串
A, 則(A)A;是語句。6
注意,A
B
並不代表
BA*。

題目思路

這題是個dp;
設dp[i][0]是長度爲i的字符串里語句的個數。
dp[i][1]是長度爲i的字符串裏代碼片段的個數。
dp[i][2]是長度爲i的字符串里語句塊的個數。
dp[i][3]是長度爲i的字符串裏函數的個數
dp[i][4]是長度爲i的字符串裏值的個數.
下面我們來逐句地解釋:(見文章末尾)

代碼

unsigned long long n;
unsigned long long dp[100010][5];
int main()
{
	cin >> n;
	dp[0][1] = dp[1][1] = dp[1][0] = 1;
	for(int i = 2; i <= n; i++)
	{
		dp[i][3] = dp[i-2][3] + dp[i-2][2];
		dp[i][2] = dp[i-2][1];
		if(i >= 4)
		{
			dp[i][3] += dp[i-4][2];
		}
		
		dp[i][0] = dp[i][2] + dp[i-1][4];
		dp[i][4] = dp[i][3] + dp[i-2][4];
		for(int j = 0; j < i; j++)
		{
			dp[i][1] += dp[j][1] * dp[i - j][0];
		}
	}
	cout << dp[n][1] << endl;
	return 0;
}

D相同的數字。題目網址

題目描述

每天早上在黑板上會寫有 n 個固定的數字,但是這些數字太無序了,所以每天晚上兔子想把他們變成相同的數字。
有兩種操作 :
1.選擇一個下標k, 兔子把aka_k 變成 ak+1a_{k+1}, 花費的時間爲c1c_1
2.選擇一個下標k, 把aka_k替換成大於aka_k的最小整數, 花費的時間爲c2c_2
兔子很懶,所以他不想花費太多的時間,你需要幫他計算出將所有數變相同的最小時間。
總共會有 q 天。兔子每天的狀態不同,所以每一天會有不同的 c1c_1c2c_2:。但是黑板上的數不會變。
第一天花費的時間當然會影響第二天的狀態。每天真實的
c1=c1(T×(lastansmod217))c_1 = c'_1\oplus (T \times (lastans \bmod 2^{17})).
c2=c1(T×(lastansmod217))c_2 = c'_1\oplus (T \times (lastans \bmod 2^{17})).
其中\oplusxorxor運, lastanslastans爲上一次的答案, 最初lastanslastans = 0;

題目思路。

其實我在考場上想出思路來了, 可是由於碼力不行QAQ, 所以沒有模擬出來, 加上對於T3dp鬼題的打擊, 我太菜了~~~~。
其實這個題有很大的切入點, 不想T3, 看起來無從下手。
這個題, 說實話, 真的, 從哪開始想都可以, 我們可以從終點考慮,或者從過程考慮, 假如叢終點, 我們會發現, 最終整個數列變成的數要不是數列中的最大值, 要麼就是大於最大值的最小質數。
再考慮怎麼跳, 顯然要讓花費的時間最小, 肯定要有策略:
首先兩個質數間最大不超過154(因爲第二種變化其實就是跳到下一個質數), 不信可以打表, 上限1e7;
然後設質數的間距爲t, 下面就是用比大小來擇優,由於考慮到後面的優化 我把一種情況的不等式列出來, 就是選c2的:
c  t  t2c \ \cdot \ t\ \ge \ t2 解得tc1/c2t \geq c1 / c2
由於是詢問, 所以對於某次詢問, 最好O(1)就能算出, 所以:
將所有質數跳的次數和跳的距離維護後綴和,然後根據c2/c1的計算找到對應的位置,o(1)即可計算。
所以問題就變爲如何統計這些數在質數上的跳躍次數和+1跳躍次數。這裏的細節比較多。
因爲n,q都比較大,所以應該預處理n,然後再線性處理q。
歐了歐了。。。
附加:最近又新改了一下, 細節都放到代碼裏了。
總之, 臨界值就是c1/c2如果比這個小, 就一步一步跳, 否則就跳質數

代碼


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e7 + 160;
const int mod = (1<<17);
 
bool isnp[MAXN];
int p[MAXN],pcnt=0;
ll sump[MAXN];//sump[]存儲到當前數爲值出現的質數的個數,其下一個質數就是p[sum[]+1]。 
ll n,q,T,a[MAXN],c[2][200],maxa,maxp,ans,lans=0,sumstep[2],behc[2][200],behs[2][200];
//c【i】[j】距離j的次數有幾次 
 //sumstep是跳一步有幾次
 //beh 後綴
void prim(int N) {
	isnp[0]=isnp[1]=1;
	for(int i=2; i<N; ++i) {
		if(!isnp[i]) {
			p[++pcnt]=i;			
		}
		sump[i]=pcnt;
		for(int j=1; j<=pcnt; ++j) {
			if((ll)i*p[j]>=N) break;
			isnp[i*p[j]]=1;
			if(i%p[j]==0) 	break;
		}
	}	
}
//求這些數到終點x的的值終點有兩種可能,到n;如果n不是質數,到n對應的下一個質數 
void count(int flag,int x){//計算出:1.需要跳多少個1步sumstep;2桶計數c[]:跳到對應的質數間距計數 
	sumstep[flag]=0;
	if(flag==0)//這些數要跳到一個合數,只能+1來跳。 
		sumstep[flag]+=(n-1)*(a[n]-p[sump[a[n]]]),a[n]=p[sump[a[n]]];
	else if(isnp[a[n]]){//可以通過+1跳到這個質數,也可以通過下一個質數跳到。
		sumstep[flag]+=p[sump[a[n]]+1]-a[n];
		c[flag][sumstep[flag]]++;
		a[n]=p[sump[a[n]]+1];
	}
	int j=sump[a[n]];
	for(int i=n;i>1;i--){
		sumstep[flag]+=(a[n]-a[i-1]); //+1來挑 
		while(p[j]>a[i-1]&&p[j-1]>=a[i-1]&&j){
			c[flag][p[j]-p[j-1]]+=(i-1);//從質數跳到質數 
			j--;
		}
		if(isnp[a[i-1]])c[flag][p[j]-a[i-1]]++; //第一步可能是合數跳到質數	
	}
	//因爲質數跳的距離越大用c2越合算,維護c[t]的後綴和,和所代替步數t*c[t]的後綴和 
	for(int i=161;i>=1;i--){
	
		behc[flag][i]=behc[flag][i+1]+c[flag][i];
		behs[flag][i]=behs[flag][i+1]+i*c[flag][i];
	}
	a[n]=x;	
} 
ll work(ll c1,ll c2){

	c1=c1^(T*lans),c2=c2^(T*lans); 
	ll t=ceil(1.0*c2/c1);
	if(t>160)t=160;
	ll sum0=(sumstep[0]-behs[0][t])*c1+behc[0][t]*c2;	
	ll sum1=(sumstep[1]-behs[1][t])*c1+behc[1][t]*c2;
	return min(sum0,sum1);	
	
} 
int main(void) {
//	freopen("in.txt","r",stdin);
	ll c1,c2;
	scanf("%lld%lld%lld",&n,&q,&T);
	for(int i=1;i<=n;i++) 
		scanf("%lld",&a[i]);
	sort(a+1,a+1+n);
	prim(a[n]+158) ;
	count(0,a[n]);
	count(1,a[n]);
	for(int i=1;i<=q;i++){
		scanf("%lld%lld",&c1,&c2);
		ans=work(c1,c2);
		lans=ans%mod;
		printf("%lld\n",ans);
	}	
}

小結

最近狀態不好, 比賽也沒打好, g估計在家裏憋的老想摸魚, 哎~~


  1. dp[1][0] = 1;
    [^2] :dp[0][1] = 1; ↩︎

  2. dp[1][1]= 1; dp[i][1] = j=0i1dp[j][1]d[ij][0]\sum_{j=0}^{i-1} dp[j][1] * d[i-j][0]
    解釋, du對於每個字符串, 代碼片段和語句都有不同的長度, 假如代碼片段長度和語句長度都已確定的話, 那麼當前長度的代碼片段個數和語句個數每個都一一對應, 因此要相乘。 ↩︎

  3. dp[i][2] = dp[i-2][1]; ↩︎

  4. dp[i][0] = dp[i][2], dp[i-2][2] + dp[i-4][2] = dp[i][3] ↩︎

  5. dp[i][3] +=dp[i-2][3], dp[i][4] = dp[i][3] + dp[i-2][3]; ↩︎

  6. dp[i][4] += dp[i-2][4]; dp[i][0] += dp[i][4];
    注意重複d[i][4] += dp[i-2][4]; ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章