【题解】牛客IOI周赛27-提高组

前言

在家闲着没事打牛客,发现自己被虐了。。。

好像别人的做法都很妙的样子。。。

A. 括号串

模拟题。

判断所给字符串合法等价于:

  1. 括号完全匹配
  2. A 串是 B 串的子序列

题意很扯淡。我最开始看到字符串长度可以比最小的小,可以删除 qwq

#include<bits/stdc++.h>
#define PII pair<int,int>
using namespace std;
const int mx=1e6+5;
int n,m,s[mx],cnt;
char a[mx],b[mx];
bool match(char x,char y) {
	return x=='('&&y==')'||x=='['&&y==']';
}
int main() {
	int T; scanf("%d",&T);
	while(T--) {
		scanf("%d%d%s%s",&n,&m,a+1,b+1);
        cnt=0;
	    for(int i=1;i<=m;i++) {
	    	if(b[i]=='['||b[i]=='(') {
	    		s[++cnt]=i;
			}
			else {
				if(cnt&&match(b[s[cnt]],b[i])) {
					cnt--;
				}
				else {
					cnt++;
					break;
				}
			}
		}
		if(cnt) {
			printf("Wrong Answer\n");
			continue;
		}
		int j=1;
		for(int i=1;i<=m;i++) {
			if(j<=n&&a[j]==b[i]) j++;
		}
		if(j==n+1) {
			printf("Accepted\n");
		}
		else {
			printf("Wrong Answer\n");
		}
	}
}

B. 虎龙斗

被虐了 qwq

首先想贪心做法,枚举 A 中选几个, B 中选 l-i 个,然后归并排序合并就完了 qwq 。然而这种贪心是错的,如下数据 :

3 3 6
1 1 1
1 1 2

显然先选下面的序列更优,然而我粗略地选了前一个就跑了导致只有 60pts。 这个时候贪心做不出来就自闭了,然后想 dp ,发现状态设计不出来。。。

后来看到一堆 O(n^2logn) 的线段树做法,自己也不会就只会 O(n^3) 的大众做法了:

仍然考虑上述贪心,有结论:选取字典序较大的那个一定更优(假若当前位相同)。

例子:

3 3 6
1 3 1
1 3 2

证明也很简单。考虑不同的那一位,假设 A<B,如果 AB 前面,那么调换两个字符串位置显然不会有影响,保证 BA 前面且答案更大,所以 A 只能在 B 后面,那么一定存在一个时刻两个串选了相同的前缀,而这是包含在先选 B 的决策中的。

#include<bits/stdc++.h>
#define PII pair<int,int>
#define INF 0x3f3f3f3f
using namespace std;
//思路:贪心 + dp 
//我太菜了 qwq...
int n,m,l,A[505],B[505],E[505],F[505],G[1005],H[1005],s[505],cnt;
vector<int> C[505],D[505];
bool chk(int *G,int *H) {
	for(int i=1;i<=l;i++) {
		if(G[i]!=H[i]) return G[i]>H[i];
		if(G[i]==-INF&&H[i]==-INF) return 0;
	}
	return 0;
}
int main() {
    memset(B,-0x3f,sizeof(B));
    memset(A,-0x3f,sizeof(A));
    memset(H,-0x3f,sizeof(H));
	scanf("%d%d%d",&n,&m,&l);
	for(int i=1;i<=n;i++) scanf("%d",&A[i]);
	for(int i=1;i<=m;i++) scanf("%d",&B[i]);
	for(int i=1;i<=n;i++) {
		int k=n-i; cnt=0;
		for(int j=1;j<=n;j++) {
			while(k&&cnt&&A[s[cnt]]<A[j]) {
				cnt--,k--;
			}
			s[++cnt]=j;
		}
		for(int j=1;j<=i;j++) {
			C[i].push_back(A[s[j]]);
		}
	}
	for(int i=1;i<=m;i++) {
		int k=m-i; cnt=0;
		for(int j=1;j<=m;j++) {
			while(k&&cnt&&B[s[cnt]]<B[j]) {
				cnt--,k--;
			}
			s[++cnt]=j;
		}
		for(int j=1;j<=i;j++) {
			D[i].push_back(B[s[j]]);
		}
	}
	for(int i=0;i<=l;i++) {
		if(i>n||l-i>m) continue;
		memset(E,-0x3f,sizeof(E)),memset(F,-0x3f,sizeof(F));
		int j,k,r;
		for(j=1;j<=i;j++) E[j]=C[i][j-1];
		for(k=1;k<=l-i;k++) F[k]=D[l-i][k-1];
		j=k=r=1;
		while(j<=i&&k<=l-i) {
			if(E[j]>F[k]) G[r++]=E[j++];
			else if(E[j]<F[k]) G[r++]=F[k++];
			else if(chk(E+j,F+k)){
				G[r++]=E[j++];
			}
			else {
				G[r++]=F[k++];
			}
		}
		while(j<=i) G[r++]=E[j++];
		while(k<=l-i) G[r++]=F[k++];
		if(chk(G,H)) memcpy(H,G,sizeof(G));
	}
	for(int i=1;i<=l;i++) printf("%d ",H[i]);
}

C. 马老师

良心出题人。。。

不妨把二进制每一位 i 拆开考虑,发现就是一个裸的背包问题。

发现可以用前缀和优化,时间复杂度 O(nlogn)

#include<bits/stdc++.h>
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int mx=5e6+5;
const int mod=1e9+7;
//背包问题 
//前缀和优化,时间复杂度 O(nlogn) 
int n,m;
int dp[mx],sum[mx],res;
int main() {
    scanf("%d%d",&n,&m);
    dp[0]=sum[0]=1;
    for(int i=0;i<25;i++) {
    	for(int j=(1<<i);j<=m;j++) {
    		sum[j]=(sum[j-(1<<i)]+dp[j])%mod;
		}
    	for(int j=(1<<i);j<=m;j++) {
    		if(j<1ll*(n+1)*(1<<i)) dp[j]=(dp[j]+sum[j-(1<<i)])%mod;
    		else dp[j]=(dp[j]+sum[j]-sum[j-(n+1)*(1<<i)])%mod;
    		if(dp[j]<0) dp[j]+=mod;
		}
    	if((1<<i+1)>m) {
    		break;
		}
	}
	printf("%d",dp[m]);
}

D. 渡摆车

话说是增广路算法。。。

咕咕咕

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值