Codeforces Round 903 (Div. 3)

[比賽鏈接]

A. Don't Try to Count

直接用string的可加性,每次 s+=s 相當於翻倍了,因爲 \(nm<=25\) 所以最多翻倍5次。

判斷什麼的直接模擬就行。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
int n,m;
string s,x;
bool check(string s,string t){
	for(int i=0;i<s.length();i++){
		for(int j=0;j<t.length();j++){
			if(i+j>=s.length()) break;
			if(s[i+j]!=t[j]) break;
			if(j==t.length()-1) return 1;
		}
	}
	return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    int t;
	cin>>t;
	while(t--){
		cin>>n>>m>>x>>s;
		for(int i=0;i<=5;i++){
			if(check(x,s)){
				cout<<i<<endl;
				break;
			}
			x+=x;
			if(i==5) cout<<-1<<endl;
		}
	}
    return 0;
}

B. Three Threadlets

根據切割的次數分類討論,可以求出每種情況 若成功則最後每段長度是多少,判斷即可。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
long long a,b,c;
bool check(long long a,long long b,long long c,long long x){
	if(a%x+b%x+c%x!=0) return 0;
	if(a<x||b<x||c<x) return 0;
	if(a/x+b/x+c/x<=6) return 1;
	return 0;
}
int main()
{
    ios::sync_with_stdio(false);
	int T;
	cin>>T;
	while(T--){
		cin>>a>>b>>c;
		long long x=a+b+c;
		if(check(a,b,c,x/3)||check(a,b,c,x/4)||check(a,b,c,x/5)||check(a,b,c,x/6)) cout<<"YES\n";
		else cout<<"NO\n";
	}

    return 0;
}

C. Perfect Square

旋轉類型的題在草稿紙上模擬一下就知道規律了。

這個題就變成了把正方形矩陣劃分成四個區域,讓四個區域的某個特定位置都變成這四個特定位置的最大值(直接看代碼方便些)。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
const int maxn=1005;
int T,n;
string s[maxn];

int main()
{
    ios::sync_with_stdio(false);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>s[i];
			s[i]=' '+s[i];
		}
		int ans=0;
		for(int i=1;i<=n/2;i++){
			for(int j=1;j<=n/2;j++){
				ans+=max(max(max(s[i][j],s[n-j+1][i]),s[n-i+1][n-j+1]),s[j][n-i+1])*4-s[i][j]-s[n-j+1][i]-s[n-i+1][n-j+1]-s[j][n-i+1];
			}
		}
		cout<<ans<<endl;
	}
    return 0;
}

D. Divide and Equalize

可以發現規律 $(a_i \div x)\times (a_j \times x) = a_i \times a_j $ 即經過這些變換之後乘積恆定不變。

所以可以將每個數質因數分解,並統計每個質因數個數,最後只需要判斷所有的質因數個數是否都是 \(n\) 的倍數(即能否平均分配到這 \(n\) 個數上)。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
const int maxn=1000005;
int vis[maxn],prime[maxn],cnt,num[500],T,n,a[maxn];
map<int,int> ma;
void work(int x){
	for(int i=1;i<=cnt;i++){
		while(x%prime[i]==0){
			x/=prime[i];
			ma[i]++;
		}
		if(x==1) break;
	}
	if(x>1) ma[x]++;
}
int main()
{
    ios::sync_with_stdio(false);
    for(int i=2;i<=2000;i++){
    	if(!vis[i]) prime[++cnt]=i;
    	for(int j=1;j<=cnt&&i*prime[j]<=2000;j++){
    		vis[i*prime[j]]=1;
    		if(i%prime[j]==0) break;
		}
	}
	cin>>T;
	while(T--){
		ma.clear();
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) work(a[i]);
		int ans=1;
		for(auto i : ma){
			if(i.second%n!=0){
				cout<<"NO\n";
				ans=0;
				break;
			}
		}
		if(ans) cout<<"YES\n";
	}
    return 0;
}

E. Block Sequence

我們發現從一個位置 \(i\) 向後可以直接跳到 \(i+a_i\) 而與 \([i+1,i+a_i]\) 區間裏的數沒有關係。

像極了dp中的狀態轉移。

需要從後往前寫dp,因爲 \(a_i\) 影響的是 \(i\) 後面的一段序列。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
const int maxn=200005;
int n,a[maxn],dp[maxn];
int main()
{
    ios::sync_with_stdio(false);
	int T;
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		dp[n]=1;dp[n+1]=0;
		for(int i=n-1;i>=1;i--){
			if(i+a[i]<=n) dp[i]=min(dp[i+a[i]+1],dp[i+1]+1);
			else dp[i]=dp[i+1]+1;
		}
		cout<<dp[1]<<endl;
	}

    return 0;
}

F. Minimum Maximum Distance

類似於換根dp。

一開始我們設 \(f[u]\)\(u\)\(u\) 爲根的子樹中標記點的最遠距離。

那麼 $f[u]=max{f[v]+1} $,即兒子節點的答案加上兒子節點到父親的距離(1)。

然後需要進行換根操作(代碼中直接延續了 \(f\) 數組,但是其意義變爲在整張圖的範圍內 \(u\) 節點的答案),我們發現父親節點的信息傳遞到某個兒子時,我們不知道父親節點的答案是不是從這個兒子那裏得到的。

所以說我們用 \(f[u][0/1]\) 分別記錄最遠距離、次遠距離。

\(f[u][0]==f[v][0]+1\) 那麼就用 \(f[u][1]+1\) 來傳遞給兒子信息,從而實現換根操作。

最後答案就是 \(min\{f[u][0]\}\)

賽場上能寫出來我還是很滿意的(

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
const int maxn=200005;
struct node{
	int v,next;
}e[maxn*2];
int cnt,p[maxn],a[maxn],ans,fa[maxn],f[maxn][2],son[maxn],vis[maxn],n,m;
void insert(int u,int v){
	cnt++;
	e[cnt].v=v;
	e[cnt].next=p[u];
	p[u]=cnt;
}
void dfs1(int u,int f){
	son[u]=0;
	fa[u]=f;
	for(int i=p[u];i!=-1;i=e[i].next){
		int v=e[i].v;
		if(v==f) continue;
		son[u]++;
		dfs1(v,u);
	}
}
void dfs2(int u){
	f[u][0]=f[u][1]=-1;
	if(vis[u]) f[u][0]=f[u][1]=0;
	for(int i=p[u];i!=-1;i=e[i].next){
		int v=e[i].v;
		if(v==fa[u]) continue;
		dfs2(v);
		if(f[v][0]==-1) continue;
		if(f[v][0]+1>f[u][0]) f[u][1]=f[u][0],f[u][0]=f[v][0]+1;
		else if(f[v][0]+1>f[u][1]) f[u][1]=f[v][0]+1;
	}
}
void gengxin(int u,int x){
	if(x==0) return;
	if(x>f[u][0]) f[u][1]=f[u][0],f[u][0]=x;
	else if(x>f[u][1]) f[u][1]=x;
}
void getans(int u){
	if(fa[u]!=-1){
		if(f[fa[u]][0]==f[u][0]+1) gengxin(u,f[fa[u]][1]+1);
		else gengxin(u,f[fa[u]][0]+1);
	}
	ans=min(ans,f[u][0]);
	for(int i=p[u];i!=-1;i=e[i].next){
		int v=e[i].v;
		if(v==fa[u]) continue;
		getans(v);
	}
}
int main()
{
    ios::sync_with_stdio(false);
    int T;
	cin>>T;
	while(T--){
		cnt=0;
		cin>>n>>m;
		for(int i=1;i<=n;i++) vis[i]=0,p[i]=-1;
		for(int i=1;i<=m;i++){
			int x;
			cin>>x;
			vis[x]=1;
		}
		for(int i=1;i<n;i++){
			int u,v;
			cin>>u>>v;
			insert(u,v);
			insert(v,u);
		}
		dfs1(1,-1);
		dfs2(1);
		ans=10000000;
		getans(1);
		cout<<ans<<endl;
	}
    return 0;
}

好,我是小丑。

看了一下題解,只需要找到距離最遠的兩個被標記的點,設距離爲 \(d\),答案就是 \(\lceil \frac{2}{d} \rceil\)

方法與找樹的直徑類似,從任意一個被標記的點出發跑一遍bfs找到距離最遠的標記點 \(i\),然後從 \(i\) 出發再跑一遍找到距離最遠的標記點 \(j\)\(ij\) 即爲所求。

代碼不放了。

G. Anya and the Mysterious String

好題。(賽後補的)

首先,因爲題目問的是區間內有無迴文串,所以我們只需要找到長度最短的迴文串就行。

要麼是形如 \(AA\) ,要麼是形如 \(ABA\)

我們把前者和後者形式的迴文串的首位置分別放在兩個set中。

假如沒有修改操作,那麼每次詢問的時候只需要分別在set裏 lowerbound 一下第一個大於等於 l 的位置,看一看該回文串右端是否大於r即可。

加上區間加的修改操作怎麼辦呢?

我們發現如果迴文串在這個區間內,那麼是不影響答案的。

所以每次修改只會影響兩端的位置的幾個位置的迴文情況。

區間修改完後單點查詢兩端位置的字符是什麼,判斷,分別更新兩個set即可。

樹狀數組(或者線段樹)都可以維護。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
const int maxn=200005;
int n,m,a[maxn],d[maxn]; 
set<int> a1,a2;
inline int lowbit(int x){
	return x&(-x);
}
void update(int x,int v){
	if(v<0) v+=(-v)/26*26;
	v=(v+26)%26;
	for(int i=x;i<=n;i+=lowbit(i)) d[i]+=v,d[i]%=26; 
}
int query(int x){
	int res=0;
	for(int i=x;i>0;i-=lowbit(i)) res+=d[i];
	return res%26;
}
void check(int x){
	if(x<=0) return;
	if(x<n){
		if(query(x)==query(x+1)) a1.insert(x);
		if(query(x)!=query(x+1)) a1.erase(x);
	}
	if(x<n-1){
		if(query(x)==query(x+2)) a2.insert(x);
		if(query(x)!=query(x+2)) a2.erase(x);
	}
}
int main()
{
    ios::sync_with_stdio(false);
	int T;
	cin>>T;
	while(T--){
		a1.clear();a2.clear();
		cin>>n>>m;
		char c;
		for(int i=0;i<=n+1;i++) d[i]=0;
		for(int i=1;i<=n;i++) cin>>c,a[i]=c-'a';
		for(int i=1;i<=n;i++) update(i,a[i]-a[i-1]);
		for(int i=1;i<n;i++) check(i);
		while(m--){
			int tp;
			cin>>tp;
			if(tp==1){
				int l,r,x;
				cin>>l>>r>>x;
				update(l,x);
				update(r+1,-x);
				check(l-2);
				check(l-1);
				check(r-1);
				check(r);
			}else{
				int l,r;
				cin>>l>>r;
				auto r1=a1.lower_bound(l);
				auto r2=a2.lower_bound(l);
				if(r1!=a1.end()&&(*r1)<=r-1){
					cout<<"NO\n";
					continue;
				}
				if(r2!=a2.end()&&(*r2)<=r-2){
					cout<<"NO\n";
					continue;
				}
				cout<<"YES\n";
			}
		}
	}
    return 0;
}

比賽總結

個人感覺比較滿意,差一題AK。

不足之處就是F題沒有找到最簡單的方法,G題對於很多細節的處理也不恰當。

現在看來基本做題思路恢復差不多了,以後需要多做做難題了,不能一直在div3和ABC划水了。

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