1. 程式人生 > 實用技巧 >CodeForces 1391 - Codeforces Round #663 (Div. 2)

CodeForces 1391 - Codeforces Round #663 (Div. 2)

昨晚被我媽強制睡覺了……今天來打個vp(不缺時效性)

CF比賽頁面傳送門

A - Suborrays

洛谷題目頁面傳送門 & CF題目頁面傳送門

給定\(n\),構造出一個\(1\sim n\)的排列\(a\)使得對於每個區間\([l,r]\)都滿足\(\mathop{\operatorname{or}}\limits_{i=l}^ra_i\geq r-l+1\)。本題多測。

\(n\in[1,100]\)。最多\(100\)組資料。

天然D2A。

顯然\([1,2,\cdots,n]\)滿足。

程式碼:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int testnum;
    cin>>testnum;
    while(testnum--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)cout<<i<<" ";
        puts("");
    }
}

B - Fix You

題目名稱->f**k you

洛谷題目頁面傳送門 & CF題目頁面傳送門

不想翻譯了,紫帆吧。

注意到顯然最後一列全要是\(\texttt D\),最後一行全要是\(\texttt R\),因為否則就走出去了。改完之後發現已經符合題意了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int N=100,M=N;
int n,m;
char a[N+1][M+5];
void mian(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i]+1;
	int cnt=0;
	for(int i=1;i<=n;i++)cnt+=a[i][m]!='D'&&a[i][m]!='C';
	for(int i=1;i<=m;i++)cnt+=a[n][i]!='R'&&a[n][i]!='C';
	cout<<cnt<<"\n";
}
int main(){
	int testnum;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}

C - Cyclic Permutations

洛谷題目頁面傳送門 & CF題目頁面傳送門

紫帆。

觀察題面中的那個例子,發現若有相對大小關係與\([2,1,3]\)\([3,1,2]\)相同的子段的話,一定是有環的。考慮否定,這樣只能有單調上升、單調下降、單峰三種長度為\(3\)的子段,不難發現這樣子的排列一定是單峰的。然後稍微看一下就會發現這樣是無環的。於是有相對大小關係與\([2,1,3]\)\([3,1,2]\)相同的子段是有環的充要條件。

直接算不會,考慮反面,就是求單峰排列的數量。列舉峰的位置,在\(i\)處時顯然有\(\dbinom{n-1}{i-1}\)種,於是總共有\(\sum\limits_{i=1}^{n}\dbinom{n-1}{i-1}=2^{n-1}\)

種。答案為\(n!-2^{n-1}\)

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n;
int main(){
	cin>>n;
	int ans1=1,ans2=1;
	for(int i=1;i<=n;i++)ans1=1ll*ans1*i%mod;
	for(int i=1;i<n;i++)(ans2<<=1)%=mod;
	cout<<((ans1-ans2)%mod+mod)%mod;
	return 0;
}

D - 505

想起了我的某個blog《404》

洛谷題目頁面傳送門 & CF題目頁面傳送門

這個好翻,翻一下(說到底還是一條懶狗)

給定一個\(n\times m\)的01矩陣,求至少要改多少個元素,使得每個邊長為偶數的正方形都包含奇數個\(1\)。或報告無解。

\(nm\in\left[1,10^6\right]\)

容易發現,若存在邊長為\(4\)的正方形,肯定是無解的。因為將它拆成\(4\)個邊長為\(2\)的正方形,小學老師教過奇+奇+奇+奇=偶。

現在只需要考慮\(n\leq 3\)\(m\leq 3\)的情況。若\(m\leq 3\),將\(n,m\)顛倒一下。

  • \(n=1\):顯然答案為\(0\)
  • \(n=2\):每列的\(1\)的數量的奇偶性顯然只有\([1,0,1,0,\cdots]\)\([0,1,0,1,\cdots]\)兩種,都算一下比個大小即可;
  • \(n=3\):每列上下兩個\(2\times1\)矩陣的\(1\)的數量的奇偶性顯然只有\(\begin{bmatrix}1&0&1&0&\cdots\\1&0&1&0&\cdots\end{bmatrix},\begin{bmatrix}0&1&0&1&\cdots\\1&0&1&0&\cdots\end{bmatrix},\begin{bmatrix}1&0&1&0&\cdots\\0&1&0&1&\cdots\end{bmatrix},\begin{bmatrix}0&1&0&1&\cdots\\0&1&0&1&\cdots\end{bmatrix}\)四種,易證每列最多修改\(1\)次,都算一下比個大小即可。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1000000;
int n,m;
vector<string> v;
bool a[4][N+1];
int main(){
	cin>>n>>m;
	if(n>=4&&m>=4)return puts("-1"),0;
	for(int i=1;i<=n;i++){
		string a;
		cin>>a;
		v.pb(a);
	}
	if(n>=4){//翻轉一下 
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[j][i]=v[i-1][j-1]^48;
		swap(n,m);
	}
	else{
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[i][j]=v[i-1][j-1]^48;
	}
//	for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)cout<<a[i][j];puts("");}
	if(n==1)puts("0"); 
	else if(n==2){
		int ans=0;
		for(int i=1;i<=m;i++)ans+=(a[1][i]^a[2][i])==(i&1);
		cout<<min(ans,m-ans);
	}
	else{
		int ans1=0,ans2=0,ans3=0,ans4=0;//四種 
		for(int i=1;i<=m;i++)ans1+=(a[1][i]^a[2][i])!=(i&1)||(a[2][i]^a[3][i])!=(i&1);
		for(int i=1;i<=m;i++)ans2+=(a[1][i]^a[2][i])==(i&1)||(a[2][i]^a[3][i])!=(i&1);
		for(int i=1;i<=m;i++)ans3+=(a[1][i]^a[2][i])!=(i&1)||(a[2][i]^a[3][i])==(i&1);
		for(int i=1;i<=m;i++)ans4+=(a[1][i]^a[2][i])==(i&1)||(a[2][i]^a[3][i])==(i&1);
		cout<<min(min(ans1,ans2),min(ans3,ans4));
	}
	return 0;
}

E - Pairs of Pairs

這是我所看了題解的題目。看完題解還厚顏無恥地在vp結束之前寫了交上去

洛谷題目頁面傳送門 & CF題目頁面傳送門

給定一個\(n\)個點\(m\)條邊的連通無向圖。你需要做以下兩件事中的任意一件:

  1. 找一條長度至少為\(\left\lceil\dfrac n2\right\rceil\)的簡單路徑;
  2. 將至少\(\left\lceil\dfrac n2\right\rceil\)個節點(要求是偶數個)兩兩分組,滿足任意兩組的四個節點的匯出子圖最多有兩條邊。

可以證明至少有一件事是可以做的。

本題多測。

\(\sum n\in\left[2,5\times10^5\right],\sum m\in\left[1,10^6\right]\)

無向連通圖有個重要的東西:DFS樹。它有一個很重要的性質:兩個點之間有邊僅當它們是長輩與晚輩的關係。很好證,因為若有非長晚輩關係的點對之間有邊,那麼當DFS到其中那個時間戳較小的點時,根據深度優先一定可以走到時間戳較大的那個點,從而使得它們是長晚輩關係,與假設矛盾。這與有向圖DFS樹的橫叉邊的性質類似。

看到第二件事中有要限制一些邊不能出現這種感覺,不難想到DFS樹的這個性質。(這個大概是Tarjan求DCC時要用到的,然鵝我還不會,所以沒想到)

求出任意DFS樹,如果存在深度\(\geq\left\lceil\dfrac n2\right\rceil\)的點那麼顯然可以做第一件事,直接輸出就行了。否則,

注意到DFS樹的這一性質,如何把它用起來。考慮兩對深度不同的兄弟節點,兄弟之間顯然不會有邊,那麼只有在不同對中的邊。考慮從下往上連,由於只能連向祖先,而上面兩個點是兄弟可以推出對於下面每個點最多隻有上面的一個點作為它的祖先,於是最多會有兩條邊。

於是問題就迎刃而解了。接下來同層的儘量分組,每層最多留下一個單身。由於最大深度\(<\left\lceil\dfrac n2\right\rceil\),單身的也是小於這麼多,可以保證被分組的點的數量。

並不能保證下次遇到這樣的智商題能做出來哦(

多測memset,爆零兩行淚。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=500000;
int n,m;
vector<int> nei[N+1];
bool vis[N+1];
int fa[N+1],dep[N+1];
void dfs(int x=1){//求搜尋樹 
	vis[x]=true;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(vis[y])continue;
		fa[y]=x;dep[y]=dep[x]+1;
		dfs(y);
	}
}
vector<int> buc[N+1];
void mian(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)nei[i].clear(),buc[i].clear(),vis[i]=fa[i]=dep[i]=0;
	while(m--){
		int x,y;
		scanf("%d%d",&x,&y);
		nei[x].pb(y);nei[y].pb(x);
	}
	dep[1]=1;
	dfs();
	for(int i=1;i<=n;i++)
		if(dep[i]>=n+1>>1){//可以輸出簡單路徑了 
			puts("PATH");
			printf("%d\n",dep[i]);
			while(i)printf("%d ",i),i=fa[i];
			puts("");
			return;
		}
		else buc[dep[i]].pb(i);
	puts("PAIRING");//不能輸出簡單路徑,那麼一定能分組 
	int cnt=0;
	for(int i=1;i<=n;i++)cnt+=buc[i].size()>>1;//統計數量 
	printf("%d\n",cnt);
	for(int i=1;i<=n;i++){//列舉層 
		for(int j=0;j+1<buc[i].size();j+=2)printf("%d %d\n",buc[i][j],buc[i][j+1]);//同層分組 
	}
}
int main(){
	int testnum;
	cin>>testnum;
	while(testnum--)mian();
	return 0;
}