「模擬賽」暑期集訓CSP提高模擬15(8.7)

_yuen發表於2024-08-08

賽時記錄:

開場看 T1,一眼 \(manacher\) 求子串迴文串,聽課是聽了,但是不會啊。跳了。

看 T2,發現結論題,推了會結論打上走了。

打完 T2 想了會 T3、T4,無思路,回來打了 T1 暴力和特殊性質,\(30pts\),又去想 T3,還是不會,這時還剩一個小時。不行,現在才 \(130pts\),包打祭的啊,能不能翻盤看我 T1 了,決定 20min 推 T1 的 \(manacher\),拼一把,根據學長講的點點記憶,10 min 還真推出來了,直接打,大概還剩半個小時時間的時候打完了 T1,過了所有樣例,以為成功了。

之後去打了 T3 暴力,以及 T4 暴力(但 T4 打一半就結束了)。

總分 \(38+82+20+0=140pts\)

T1 我的所謂正解只拿了 \(38pts\),輸出答案部分有個細節寫錯了;

T2 也是細節寫錯了。

A.串串 38pts

原題:THUPC2018 綠綠和串串

自己賽時手推的 \(manacher\) 以及手打的板子,賽後檢驗沒問題,沒 A 是因為輸出答案部分沒有特判這個迴文串既不接頭也不接尾的情況。

不是,怎麼我自己打的 \(manacher\) 的程式碼那麼長呢?

這是賽時自己打的 manacher
void manacher(){
	int tail = 0, whi = 0;
	for(int mid=2; mid<=n-1; mid++){
		if(tail > mid){
			int con = whi - (mid - whi);
			if(f[con] + mid < tail) f[mid] = f[con];
			else{
				f[mid] = tail - mid;
				for(int len=1; len<=min(n-tail, mid-f[mid]-1); len++){
					if(s[tail+len] != s[mid-(tail-mid)-len]) {f[mid] += len - 1;break;}
					if(len == min(n-tail, mid-f[mid]-1)) f[mid] += len;
				}
			}
		}
		else{
			for(int len=1; len<=min(n-mid, mid-1); len++){
				if(s[len+mid] != s[mid-len]) {f[mid] = len - 1; break;}
				if(len == min(n-mid, mid-1)) f[mid] = len;
			}
		}
		if(f[mid] and tail < mid+f[mid])
			tail = mid + f[mid], whi = mid;
		// cout<<mid<<": "<<f[mid]<<"\n";
	}
}
這是部落格園粘的板子
int manacher()
{
      int len=init();
      int ans=-N,id,mx=0;
      for(int i=1;i<len;i++)
      {
          if(i<mx)p[i]=min(p[id*2-i],mx-i);
          else p[i]=1;
          while(news[i-p[i]]==news[i+p[i]])p[i]++;
          if(mx<i+p[i])id=i,mx=i+p[i];
          ans=max(ans,p[i]-1);
      }
      return ans;
}

正解:

  1. manacher 求每一個位置為迴文中心的最長迴文子串;

  2. 雜湊 求每一個迴文串的長度。

兩個方法都是為了求出所有迴文子串。

然後列舉每個迴文子串,

  • 如果這個子串末尾是整個串的最末位,那麼這個串是合法的(直接以末位翻轉可以形成原串);
  • 如果這個子串是從整個串最首位開始的,且它也可作為另一個合法發回文子串的前半段,那麼這個子串也合法。(翻轉之後形成的串還可以再翻轉直到形成原串)

code:

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define love cout<<"---------\n";
using namespace std;

const int N = 1e6 + 10;

int T, n, f[N], can[N];
char s[N];

void manacher(){
	int tail = 0, whi = 0;
	for(int mid=2; mid<=n-1; mid++){
		if(tail > mid){
			int con = whi - (mid - whi);
			if(f[con] + mid < tail) f[mid] = f[con];
			else{
				f[mid] = tail - mid;
				for(int len=1; len<=min(n-tail, mid-f[mid]-1); len++){
					if(s[tail+len] != s[mid-(tail-mid)-len]) {f[mid] += len - 1;break;}
					if(len == min(n-tail, mid-f[mid]-1)) f[mid] += len;
				}
			}
		}
		else{
			// cout<<s[1+mid]<<" "<<s[mid-1]<<" ";
			for(int len=1; len<=min(n-mid, mid-1); len++){
				if(s[len+mid] != s[mid-len]) {f[mid] = len - 1; break;}
				if(len == min(n-mid, mid-1)) f[mid] = len;
			}
		}
		if(f[mid] and tail < mid+f[mid])
			tail = mid + f[mid], whi = mid;
		// cout<<mid<<": "<<f[mid]<<"\n";
	}

	can[n] = true;
	for(int i=n-1; i>=2; i--){
		if(f[i]+i == n) can[i] = true;
		else if(can[f[i]+i] and i-f[i]==1) can[i] = true;
	}
	for(int i=2; i<=n; i++){
		if(can[i]) cout<<i<<" ";
	}cout<<"\n";
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>T;
	while(T--)
	{
		cin>>(s+1); n = strlen(s+1);
		if(n == 1){cout<<1<<"\n";continue;}
		manacher();
		for(int i=1; i<=n; i++) f[i] = can[i] = 0;
	}


	return 0;
}

B.排排

結論推的沒問題,但是判一個位置 \(x\) 之後 \(x+1\) ~ \(n\) 全都出現過判錯了。

題面:

image

正解:

只有 0 ~ 3 四個答案:

  • 序列本來就是有序的,答案為 0;

  • 存在一個位置 \(i\) 保證 \(a_i=i\) 並且 \(i\) 之前出現了 \([1, i-1]\) 區間內的所有數時,答案為 1;(只在 \(i\) 位操作一次即可)

  • 1 在 \(n\) 位上,\(n\) 在 1 位上時,答案為3;(先看答案為 2 的情況。此時的情況我們可以隨便在不是 1 和 \(n\) 的位置操作一次使得出現答案為 2 的情況再按答案為 2 的情況操作兩次)

  • 其餘答案為2;(\(n\) 不在第 1 位上時,我們在第 1 位操作一次,使得 \(n\) 可以到第 \(n\) 位;再在 \(n\) 上操作一次即可。若 1 不在 \(n\) 位上,一樣)

code :

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

const int N = 2e5 + 10;

int T, n, a[N];
int head[N], tail[N];

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>T;
	while(T--)
	{
		cin>>n;
		bool f0 = true;
		for(int i=1; i<=n; i++){
			cin>>a[i];
			if(a[i] != i) f0 = false;
			head[i] = false;
		}
		if(f0){cout<<"0\n";continue;}

		if(a[1] == n and a[n] == 1){cout<<"3\n"; continue;}

		int h = a[1], t = a[1] - 1;
		for(int i=2; i<=n; i++){
			if(a[i] < h) t--;
			else if(a[i] > h + 1) t += a[i] - h - 1;
			h = max(a[i], h);
			if(!t) head[i] = true;
		}

		int w = a[n]; t = n - a[n];
		for(int i=n-1; i>=1; i--){
			if(a[i] > w) t--;
			else if(a[i] < w - 1) t += w - 1 - a[i];
			w = min(w, a[i]);
			if(!t and head[i]){
				t = -180; break;
			}
		}
		if(t == -180){cout<<"1\n"; continue;}
		if(a[1] == 1 or a[n] == n){cout<<"1\n"; continue;}

		cout<<"2\n";
	}

	return 0;
}

相關文章