2021 Hubei Provincial Collegiate Programming Contest(补题)

A:CRC Tes

傻逼题意 恶心模拟

D:Fragmentation merging(思维)

题意:

一个长度为n的排列数组。

选取一个区间 [l_{1},r_{1}](1<=l_{1}<=n,1<=r_{1}<=n) 的数作为集合A,选取一个区间 [l_{2},r_{2}](1<=l_{2}<=n,1<=r_{2}<=n) 里面的数作为集合B,当两个区间的交集为空,而且A!=B  并集为C,  c_{max}-c_{min}+1=C的元素的个数,该超级集合C就是合法的。

当我们选取的区间中l>r的时候,该集合为空集。

求集合C的数目

思路:

(感觉难点在于读懂题意)

一定要注意到当我们选取的区间中l>r的时候,该集合为空集。

这样才能推出样例

容易发现c_{max}-c_{min}+1=C的元素的个数这个条件,你选取的区间一定是一段连续的数。

那我们直接枚举每一段区间 然后检查需要多少个区间才能拼出所枚举的区间,如果需要>2的区间才能拼出,一定是不满足的,因为我们只能选取两个集合,统计一下满足<=2的区间个数即可。

#include<bits/stdc++.h>
// #define int long long
using namespace std;
const int N=3e5+10;
vector<int>ban[5010];
int a[5010];
int pos[5010];
bool res[5010];
signed main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[a[i]]=i;
        }
        if(n==1)
        {
            cout<<0<<endl;
            continue;
        }
        else
        {
            int ans=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                res[j]=0;
                int jd=0;
                for(int j=i;j<=n;j++)
                {
                    int wz=pos[j];
                    if(wz==1)
                    {
                        if(res[wz+1]==0)
                        jd++;
                    }
                    else if(wz==n)
                    {
                        if(res[wz-1]==0)
                        jd++;
                    }
                    else
                    {
                        if(res[wz-1]==1&&res[wz+1]==1)
                        jd--;
                        else if(res[wz-1]==0&&res[wz+1]==0)
                        jd++;
                    }
                    res[wz]=1;
                    if(jd<=2)
                    {
                        ans++;
                    }
                }
            }
            cout<<ans<<endl;
        }
    }
}

 F:Battery

两个数列都排一下序,然后从小到大贪心的用电池。

#include<bits/stdc++.h>
#define ll long long
#define debug2(x,y) cout<<"**"<<x<<" "<<y<<endl;
using namespace std;
signed main(){
	int n,m;
	scanf("%d%d",&n,&m);
	vector<int>a(n+1),b(m+1);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%d",&b[i]);
	}
	sort(a.begin(),a.end());
	sort(b.begin(),b.end());
	int j=1;
	for(int i=1;i<=n;i++){
		while(a[i]&&j<=m){
			if((a[i]-b[j])>=0){
				a[i]-=b[j];
				j++;
			}
			else{
				break;
			}
		}
	}
	printf("%d\n",j-1);
}

H:CRC Tes(最短路)

题意:

给定一个有向图,初始边长度为1,对于每个长度小于等于3的环,环上边长度变为0。该操作可以进行多次,求最后从1号点的最短路

思路:

枚举每一个点,所能到达的长度不大于3的环,对其缩点更新距离为0,用fa[]记录更新为这个环属于同一集合。然后跑一边dijkstra,属于同一集合它们之间的距离为0。

#include <bits/stdc++.h>
// #define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 310, M = 30010;
int h[N], ne[M], e[M], idx;
int fa[N];
bool g[N][N]; // 记录a是否能到达b
int n, m;
int dist[N];
bool st[N];
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void sd(int u)
{
	vector<int>pos;
	for(int i=h[u];~i;i=ne[i])
	{
		int v=e[i];
		if(g[fa[v]][u])//u->v->u
		{
			fa[v]=fa[u];
			pos.push_back(v);
		}
		for(int j=h[v];~j;j=ne[j])
		{
			int c=e[j];
			if(g[fa[c]][u])//u->v->c->u
			{
				fa[c]=fa[u];
				fa[v]=fa[u];
				pos.push_back(v);
				pos.push_back(c);
			}
		}
	}
	for(auto v:pos)
	{
		for(int i=h[v];~i;i=ne[i])
		{
			int k=e[i];
			g[fa[u]][k]=1;
		}
	}
}
void dijkstra()
{
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	priority_queue<PII, vector<PII>, greater<PII>> q;
	q.push({0,1});
	while(q.size())
	{
		auto fr=q.top();
		q.pop();
		int u=fr.second,dis=fr.first;
		if(st[u])
		continue;
		st[u]=true;
		for(int i=h[u];~i;i=ne[i])
		{
			int v=e[i];
			if(st[v])
			continue;
			int w=1;
			if(fa[v]==fa[u])
			w=0;
			if(dist[v]>dis+w)
			{
				dist[v]=dis+w;
				q.push({dist[v],v});
			}
		}
	}
}
int main()
{
	memset(h, -1, sizeof h);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b);
		g[a][b] = 1;
	}
	for (int i = 1; i <= n; i++)
		fa[i] = i;
	for(int i=1;i<=n;i++)
	sd(i);
	dijkstra();
	for(int i=1;i<=n;i++)
	{
		if(dist[i]==0x3f3f3f3f)
		cout<<-1<<" ";
		else
		cout<<dist[i]<<" ";
	}
}

I:Sequence(单调栈)

这个问题可以转换为有个n阶2维矩阵,有些点被挖去,求有多少个矩形?

比如 1禁止出现2 3数字,那么这个矩阵g[1][2],g[1][3]会被挖去。

我们把每一个不能占据格子标1,可以的标0.

我们一行一行的枚举,可以递推出能向上延申的高度,对于每一行,从左往右扫,维护一个单调栈,栈中按高度从低到高,同时维护每个高度向右延申的长度。

每加进一个元素,弹出比他更高的元素,并合并长度。以这个点为右下角的矩形个数就是栈中所有的高度×长度之和  (来自题解)

感性的理解 枚举到的每一个格子元素向左延申都应该产生贡献(前面矩形面积之和以及自己),因为是递增的。

比如求下面这个图像有多少个完整的矩形 答案为15

手动数一数:

1*1有6个 1*2有6个 1*3有2个 2*2有1个 共15个

下面模拟一下:我们用val统计一下每一行的贡献

首先我们在第一行val=0

有一个矩阵  val+=1*1,ans+=val

接下来第二行val=0

高度为1的矩阵进栈 val+=1*1=1,ans+=val(2)

高度为2的矩阵进栈 比高度为1的矩阵高  val+=(2*1)=3这个矩阵的面积 ans+=val(5)

接一下第三行val=0

高度为2的矩阵进栈 val+=2*1=2,ans+=val(7)

高度为3的矩阵进栈  比高度为2的矩阵高 val+=(3*1)=5,ans+=val(12)

高度为1的矩阵进栈 比高度为3的低 则高度为3的pop出去 比高度为2的还低 继续pop出去,同时需要加上pop出去的矩形宽度,val值也应减去对应矩形所产生的贡献(5-2-3)=0,然后高度为1,宽为3的矩阵进栈,val+=(1*3)=3,ans+=val(15)

结束

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5010;
int g[N][N];
int h[N];//高
signed main()
{
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%lld%lld",&a,&b);
        g[a][b]=1;
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(g[i][j])
            h[j]=0;
            else
            h[j]++;
        }
        stack<pair<int,int>>st;
        int mj=0;//面积
        for(int j=1;j<=n;j++)
        {
            int len=1;
            while(!st.empty()&&st.top().first>h[j])
            {
                len+=st.top().second;
                mj-=(st.top().first*st.top().second);
                st.pop();
            }
            mj+=len*h[j];
            st.push({h[j],len});
            ans+=mj;
        }
    }
    cout<<ans;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值