1. 程式人生 > 其它 >Codeforces Round #789 (Div. 2)

Codeforces Round #789 (Div. 2)

簽到題沒啥好說的

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=105;
int T;
int a[maxn];
void solve(); 
int main(){
	cin>>T;
	while(T--)solve();
     return 0;
}
void solve(){
	int n;
	int res=0;cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]==0)res++;
	}
	if(res)cout<<n-res<<endl;
	else{
		sort(a+1,a+1+n);
		bool pd=1;
		for(int i=1;i<=n;i++)
		if(a[i]==a[i-1])
		pd=0;
		if(pd)cout<<n+1<<endl;
		else cout<<n<<endl;
	}
}

好像可以貪心不過我一眼看去就直接寫了dp

dp[i][j][k] 其中j為0/1表示當前為‘1’還是‘0’ k為0/1表示當前狀態合法還是不合法

表示前i個 第i位置個為j 狀態為k

合法一定是由不合法轉移過來的 不合法只能從合法轉移過來

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=2e5+5;
int dp[maxn][2][2];// [1][1]當前為1 合法 [1][0]當前為0 合法 [0][1] 當前為1 不合法 [0][0] 當前為0 不合法 
char s[maxn];
int T;
void solve();
int main(){
	cin>>T;
	while(T--)solve();
     return 0;
}
void solve(){
	int n;
	cin>>n;
	scanf("%s",s+1);
	memset(dp,0x7f,sizeof(dp));
	dp[0][0][0]=dp[0][1][0]=dp[0][1][1]=dp[0][0][1]=0;
	for(int i=1;i<=n;i++){
		dp[i][1][1]=dp[i-1][1][0]+(s[i]=='0');
		dp[i][0][1]=dp[i-1][0][0]+(s[i]=='1');
		dp[i][1][0]=min(dp[i-1][0][1],dp[i-1][1][1])+(s[i]=='0');
		dp[i][0][0]=min(dp[i-1][1][1],dp[i-1][0][1])+(s[i]=='1');
	}
	cout<<min(dp[n][0][1],dp[n][1][1])<<endl;
}

這個題就上一個題目的加強版 我們只需要在轉移的時候順便記錄一下就好

這個題要用雙線並行 卡常數 沒啥意思 我下面的程式碼TLE了

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=2e5+5;
int dp[maxn][2][2],num[maxn][2][2];
// [1][1]當前為1 合法 [1][0]當前為0 合法 [0][1] 當前為1 不合法 [0][0] 當前為0 不合法 
char s[maxn];
int T,res;
void solve();
int main(){
	scanf("%d",&T);
	while(T--)solve();
     return 0;
}
void solve(){
	int n;
	scanf("%d",&n);
	scanf("%s",s+1);
	memset(dp,0x7f,sizeof(dp));
	memset(num,0,sizeof(num));
	dp[0][0][0]=dp[0][1][0]=dp[0][1][1]=dp[0][0][1]=0;
	for(int i=1;i<=n;i++){
		dp[i][1][1]=dp[i-1][1][0]+(s[i]=='0');
		num[i][1][1]=num[i-1][1][0];
		dp[i][0][1]=dp[i-1][0][0]+(s[i]=='1');
		num[i][0][1]=num[i-1][0][0];
		if(dp[i-1][0][1]<dp[i-1][1][1])
		num[i][1][0]=num[i-1][0][1]+1,dp[i][1][0]=dp[i-1][0][1]+(s[i]=='0');
		else num[i][1][0]=num[i-1][1][1],dp[i][1][0]=dp[i-1][1][1]+(s[i]=='0');
		if(dp[i-1][1][1]<dp[i-1][0][1])
		num[i][0][0]=num[i-1][1][1]+1,dp[i][0][0]=dp[i-1][1][1]+(s[i]=='1');
		else num[i][0][0]=num[i-1][0][1],dp[i][0][0]=dp[i-1][0][1]+(s[i]=='1');
	}
	if(dp[n][0][1]<dp[n][1][1])
	res=num[n][0][1];
	else res=num[n][1][1];
	printf("%d %d\n",min(dp[n][0][1],dp[n][1][1]),res+1);
}

開始我想差分樹狀陣列去維護 但是發現有一左一右兩個限制 應該是可以維護出來的 但是我不知道怎麼維護

像這種題其實就是先定一邊 再計算

考慮先列舉斷點i 穿過i的區間很好計算 但是沒有穿過的呢?

我們只要依次處理右邊為斷點i的就好

描述不好描述 程式碼一看就能明白 這個題的解題思路很牛逼

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=5e3+5;
int T;
ll dp[maxn],a[maxn],pre[maxn];
void solve();
int main(){
	cin>>T;
	while(T--)solve();
     return 0;
}
void solve(){
	int n;cin>>n;
	ll res=0;
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=1;i<n;i++)
	for(int j=i+1;j<=n;j++)
	if(a[i]>a[j])dp[i]++;
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++)
		if(a[j]>a[i])dp[j]--;
		pre[0]=0;
		for(int j=1;j<i;j++)pre[j]=pre[j-1]+dp[j];
		for(int j=1;j<i;j++)
		if(a[j]<a[i])
		res+=(pre[i-1]-pre[j]);
	}
	cout<<res<<endl;
}