[省赛] 2021 CCPC 新疆 ABCDE

前言

传送门 :
翻译水瓶真的不够,太影响做题了

勉强水了个铜,这场并没有多少体验

需要加强训练的地方 :

  1. 优先队列 处理区间问题
  2. Tarjan缩点
  3. 翻译水瓶
  4. 二分
  5. 3小时的阀座训练
  6. CF的训练

A.基础二分

题目意思就是摘气球,前面摘了后面就不可以摘了

求一遍二分就行

心路历程 :

这题虽然说一开始看完之后就知道是二分,但是还是做了一个小时我才A出来,
实在是太废了

1.实在是太紧张了
2.对于lower_bound和upper_bount以及greater<int> 不是非常熟练
3.对于手写二分 完全不熟悉

CODE
void solve()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i];
    }

    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    mp[a[i]] = i;


    for(int i=1;i<=m;i++)
    cin>>h[i];
    sort(h+1,h+1+m);
    for(int i=1;i<=n;i++)
    {
        int d = upper_bound(h+1,h+1+m,a[i]) - h;
        d--;
        if(d >= m)
        {
            d = m;
        }
        ans[i] = d;
        temp[i] = d;
        ans[i] -= temp[i-1];
        mp[a[i]]=ans[i];
        if(ans[i]<0)
        ans[i] = 0;
    }

    for(int i=1;i<=n;i++)
    {
        cout<<mp[b[i]]<<endl;
    }
}

B.基础线性DP

看完之后 就是很裸的一个线性DP问题

我们可以 反着处理 因此状态转移方程就很简单了

f [ i ] = m a x ( f [ i − d − 1 ] + w [ i ] , f [ i − 1 ] ) f[i] = max(f[i-d-1]+w[i],f[i-1]) f[i]=max(f[id1]+w[i],f[i1])

心路历程 :
虽然一开始就可以直接想到状态转移方程
但是还是A了一个小时才做出来

  1. 因为A题太紧张,脑袋涣散了
  2. 不小心写错一个变量导致 O n n n 做法出错,否定自己,然后写了一个O n 2 n^2 n2
  3. 结果优化了好久的 O n 2 O n^2 On2 最后才被队友提醒
CODE
void solve()
{
    int n,d,m;
    cin>>n>>d>>m;

    for(int i=1;i<=n;i++)
    {
        cin>>w[i];
        f[i] = 0;

    }
    reverse(w+1,w+1+n);
    reverse(f+1,f+1+n);

    for(int i=1;i<=n;i++)
    {
        if(w[i]>m)
        {   
            if(i-d-1>=1)
            f[i] = max(f[i-1],f[i-d-1]+w[i]);
            else 
            f[i] = max(f[i-1],w[i]);
            continue;
        }
        f[i] = f[i-1]+w[i];
    }
    cout<<f[n]<<endl;
}

C.Tarjan缩点+拓扑

题意

不懂,完全不懂
在这里插入图片描述

CODE
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 2e6+10, M =  5e4+10;

vector<int> g[N];
stack<int> stk;
int in_stk[N];
int scc_cnt;

int tempstamp;
int dfn[N],low[N];
int id[N],size_scc[N];

int in[N];
int n,m;

///dfn[i] 遍历到i 的时间戳
///low[i] 从u开始走,能遍历到的最小时间戳
///如果u是其所在的强连通分量最高点 那么 dfn[u] = low[u]

/**
如果 i 和 j 不在同一个 scc(强连通分量) 当中 加 id[i] ->id[j]

**/
void tarjan(int u)
{
    dfn[u] = low[u] = ++tempstamp;

    stk.push(u);
    in_stk[u] = 1;


    for(auto x:g[u])
    {
        if(!dfn[x])
        {
            tarjan(x);
            low[u] = min(low[u],low[x]);
        }
        else if(in_stk[x])
        {
            low[u] = min(low[u],dfn[x]);
        }
    }

    if(dfn[u] ==low[u])
    {
        ++ scc_cnt;
        int y;
        do
        {
            y = stk.top();
            stk.pop();

            in_stk[y] = 0 ;
            id[y] = scc_cnt;
            size_scc[scc_cnt] ++;
            ///对于每一个scc连通块的编号
        }
        while(y!=u);
    }
}

vector<int> g2[N];
int in2[N];
ll dist[N];

void topsort()
{
    queue<int> q;

    for(int i=1; i<=scc_cnt; i++)
        if(!in2[i])
        {
            q.push(i);
            dist[i] = size_scc[i];
        }

    while(!q.empty())
    {
        int t= q.front();
        q.pop();

        for(auto x :g2[t])
        {
            dist[x] =  max(dist[x],dist[t]+size_scc[x]);
            --in2[x];
            if(!in2[x])
                q.push(x);
        }
    }
}
void solve()
{
    cin>>n>>m;
    while(m -- )
    {
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
    }

    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
        {
            tarjan(i);
        }
    }

    ///建立DAG 跑拓扑排序

    for(int i=1; i<=n; i++)
    {
        for(auto x : g[i])
        {
            int a = id[i];
            int b = id[x];
            if(a!=b)
            {
                g2[a].push_back(b);
                in2[b]++;
            }
        }
    }

    topsort();
    ll ans = 0;
    
    for(int i=1; i<=scc_cnt; i++)
        ans= max(ans,dist[i]);
    cout<<ans<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    ///int t;cin>>t;while(t -- )
    solve();
    return 0;
}

D.优先队列处理区间

前言

属于是 知识盲区了,或者是 虽然会用但是不会这么用

思路

对于这题无非就是 从一个大区间开始减,左边右边都减

然后我们可以用 m a p map map 和 大根堆进行 操作就行了

具体看代码

CODE
struct node
{
	int l,r;
	ll sum;

	friend bool operator< (node x,node y )
	{
		return x.sum<y.sum;
	}
};

ll a[N],sum;


void solve()
{
	cin>>n>>m;
	for(int i = 1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}

	priority_queue<node> q;

	q.push({1,n,sum});
	mp[{1,n}] = 1;

	while(m)
	{
		auto t = q.top();
		q.pop();

		m --;
		cout<<t.sum<<" ";
		if(t.l != t.r)
		{
			if(!mp[{t.l +1 ,t.r}])
			q.push({t.l+1,t.r,t.sum - a[t.l]});
			if(!mp[{t.l,t.r-1}])
			q.push({t.l,t.r-1,t.sum-a[t.r]});

			mp[{t.l+1,t.r}] = mp[{t.l,t.r-1}] = 1;
		}
	}
}

E.模拟?

前言

补完题,笑尿了…
大概CF div2D ? 或者 C

思路

观察范围 :
a [ i ] < = 5000 a[i] <= 5000 a[i]<=5000 ∑ a [ i ] < = 5000 \sum a[i] <= 5000 a[i]<=5000

这说明了什么呢,说明除0外,我们最多只有5000的值

也就是实际上 n < 1 e 5 n<1e5 n<1e5但是呢 n < = 5000 n<=5000 n<=5000

所以我们可以 O n 2 O n^2 On2的处理

我们使用 两个数组 处理非0的下标 然后我们 N 2 N^2 N2的求出答案即可

对于 0 0 0的数组,我们只需要在输出答案的时候输出 m a x n = m a x ( a , b ) maxn= max(a,b) maxn=max(a,b)就行了

CODE
void solve()
{
	cin>>n;
	int maxn  = 0 ;
	for(int i = 0 ;i<n;i++)
	{
		cin>>a[i];
		if(a[i])
		s1.pb(i), maxn = max(maxn,a[i]);
	}
	
	for(int i = 0;i<n;i++)
	{
		cin>>b[i];
		if(b[i])
		s2.pb(i), maxn = max(maxn,b[i]);
	}
	
	for(int i=0;i<s1.size();i++)
		for(int j=0;j<s2.size();j++)
		{
			int k  = (s1[i]+s2[j])%n;
			c[k] =max(c[k],a[s1[i]]+b[s2[j]]);
		}
		
	for(int i=0;i<n;i++)
	cout<<max(c[i],maxn)<<" ";
}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值