"蔚来杯"2022牛客暑期多校训练营2部分题题解

一上去就被这道构造体给搞趴下了....
造了一会的数据,发现sqrt(n)好像是可以构造出来的。我们只需要将n个数按照sqrt(n)个分组,倒序排列即可。例如n=9时,我们可以构造:789,456,123。使得他们组内升序,组外降序。
证明的话参考题解:
对于排列中的每个元素,我们记一个二元组(\(lis_i,lds_i\))表示以第i个数结尾的lis,lds。
那么对于这个排列生成的n个二元组而言。必定会两两不同。(证明的话,考虑反证法,若存在相同的两个二元组位置下标是i,j。则若a[j]>a[i]。那么必存在\(lis_j>lis_i\).反之同理。)
所以考虑这其中二元组中的最大值。必定是sqrt(n)。

这个DP我又歇菜了。。。
感觉和上一年牛客一样,每次这种求方案数的DP的问题都会让我歇菜....
https://ac.nowcoder.com/acm/discuss/blogs?tagId=148805
这个题解写的不错...赞!
image
我觉得这点考虑是最重要的。关于子序列S构造原序列T的方案数.我们必须保证以上的条件。这样方便我们集合的划分。
我们设f[i][j][k]表示原序列的第i位,已经匹配过了子序列的第j位,并且此时在原序列中,左括号比右括号多k的方案数。
考虑初始化.
在我们匹配子序列的第一个字符前,我们怎么放都行。只要保证左括号>=右括号即可。
f[0][0][0]=1;
f[i][0][k]=f[i-1][0][k-1]+f[i-1][0][k+1];
考虑平时的转移:
对于第i个字符,有两种划分。
1.第i个字符对应子序列中的第j个字符.那么只有一种方法。
2.第i-1及其之前的字符已经匹配过第j个字符,那么只有一种方法。(这里值得思考为什么只有一种方法,因为子序列的第j个字符我们已经在前面匹配过了,所以j-j+1这两个字符匹配之间我们不能有任何字符与j+1相同.所以只能选择与j+1相反的符号了。)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,M=1010,P=1e9+7;
char c[M];
int n,m;
ll f[N][N][N];
//原序列的i已经匹配过了子序列的j,左括号-右括号=k的方案数. 
inline void solve()
{
	scanf("%d%d",&n,&m);
	scanf("%s",c+1);
	for(int i=0;i<=m;++i)
		for(int j=0;j<=m;++j)
			for(int k=0;k<=m;++k) f[i][j][k]=0;
	f[0][0][0]=1;
	for(int i=1;i<=m;++i)	
		for(int j=0;j<=i;++j)
		{
			f[i][0][j]+=(j-1<0?0:f[i-1][0][j-1])+f[i-1][0][j+1];
			f[i][0][j]%=P;
		}
	for(int i=1;i<=m;++i)
		for(int j=1;j<=n;++j)
			for(int k=0;k<=i;++k)
			{
				int d=((c[j]=='(')?1:-1);//左括号1,右括号-1. 
				if(k-d>=0) f[i][j][k]+=f[i-1][j-1][k-d];
				//考虑第i位匹配第j位.	
				if(k+d<=m) f[i][j][k]+=f[i-1][j][k+d];
				//考虑i-1已经匹配第j位了.我们只能与第j位相反了. 
				f[i][j][k]%=P;
			}	
	printf("%d\n",f[m][n][0]);
}
int main()
{
//	freopen("1.in","r",stdin);
	int T;scanf("%d",&T);
	while(T--) solve();
	return 0;
} 

H Take the Elevator

刚开始理解错题意了...
考虑整个电梯最终的运作肯定是从一层到某一层,再返回到一层,一直往返,知道没有人为止。
那么我们的想法就是尽量让这个次数足够少,每次上升的最高层尽量小。发现上升和下降的完全不相干,我们分开考虑。
考虑上升的话,上升的最高层显然由,(x,y)最大的y决定的。于是我们可以将所有上升的人按照y降序排序.先将最高的m个人塞进去。之后考虑能不能继续塞进去。如果某(t)个人的y小于等于某人(u)的x的话。那我们u再进电梯前,完全可以让t先上。也就是说对于没上电梯的(x,y)而言,如果y《上电梯的x的话,就可以被塞进去。考虑有多个可以塞的时候,我们显然塞r大的最优。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
int n,m,k;
struct node
{
    int l,r;
    bool friend operator <(node a,node b) 
    {
        return a.r>b.r;
    }
};
multiset<node>up,down;
inline void solve()
{
    priority_queue<ll>q;
    for(int i=1;i<=m&&up.size();++i)//前m大的l都放进去. 
    {
        q.push((*up.begin()).l);
        up.erase(up.begin());
    }
    while(up.size())//往里面塞人. 
    {
        ll x=q.top();q.pop();
        auto id=up.lower_bound(node({x,x}));
        if(id==up.end()) break;
        up.erase(id);
        q.push((*id).l);
    }
    while(q.size()) q.pop();
    for(int i=1;i<=m&&down.size();++i)//前m大的r都放进去. 
    {
        q.push((*down.begin()).l);
        down.erase(down.begin());
    }
    while(down.size())//往里面塞人. 
    {
        ll x=q.top();q.pop();
        auto id=down.lower_bound(node({x,x}));
        if(id==down.end()) break;
        down.erase(id);
        q.push((*id).l);
    }
}
int main()
{
//  freopen("1.in","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        if(l<r) up.insert(node({l,r}));
        else down.insert(node({r,l}));
    }
    ll ans=0;
    while(up.size()||down.size())
    {
        int d=0;
        if(up.size()) d=max(d,(*up.begin()).r);
        if(down.size()) d=max(d,(*down.begin()).r);
        ans+=2ll*(d-1);
        solve();
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2022-07-25 10:46  逆天峰  阅读(31)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//