Codeforces Round #802 (Div. 2)

C题-Helping the Nature

在这里插入图片描述
在这里插入图片描述

思路

  • 直接的思路:使得每棵相邻树的高度相同。做法只能是对每棵相邻的树,根据大小处理前缀或后缀之一,相同高度为两者的较小值,且这些处理序列一个也不能少。
  • 差分的思路(转):
    在这里插入图片描述其中 d 1 = a [ 1 ] − a [ 0 ] d_1 = a[1] - a[0] d1=a[1]a[0],其中 a [ 0 ] = 0 a[0]=0 a[0]=0

看到区间操作就想到前缀和和差分。属于常用套路

#include <bits/stdc++.h>
using namespace std;
#include<stack>
#define int long long
signed main()
{
    int t;
    cin >> t;
    for(int i = 0;i<t;i++)
    {
        int n;
        cin >> n;
        int a[n];
        for(int k = 0;k<n;k++) cin>>a[k];
        int ans = 0;
        int temp = a[n - 1];
        for(int k = n - 1;k>=1;k--)
        {
            if(a[k] - a[k - 1] >= 0)  //后缀相减使其等于前缀
            {
                ans += (a[k] - a[k - 1]);
                temp -= (a[k] - a[k - 1]);
            }
            else
            {
                ans += (a[k - 1] - a[k]);
            }
        }
        cout << ans + abs(temp) << endl;
    }
    system("pause");
}

D题-Helping The Nature

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路

  • 必要条件减小搜索空间
  • 找是否能满足DP

D P [ i ] DP[i] DP[i],处理到第i个lock,前i个lock全部开启,并装满前i个lock所需要的最短时间。guess:

  • 若第i个lock能在前面i-1个lock装满的时间之前或刚好装满,则 D P [ i ] = D P [ i − 1 ] DP[i] = DP[i-1] DP[i]=DP[i1]
  • 否则,前i-1个lock溢出的水与第i个lock管道的水会相互混合。可看作是前i个管道向前i个lock注水。所需时间 ⌈ s u m ( v [ 1 : i ] ) i ⌉ \lceil \frac{sum(v[1:i])}{i}\rceil isum(v[1:i])

所以 D P [ i ] = m a x ( D P [ i − 1 ] , ⌈ s u m ( v [ 1 : i ] ) i ⌉ ) DP[i] = max(DP[i-1],\lceil \frac{sum(v[1:i])}{i}\rceil) DP[i]=max(DP[i1],isum(v[1:i]))
对每个query,必要条件是 t q u e r y > = d p [ n ] t_{query} >= dp[n] tquery>=dp[n].在这样的时间段内,若取q个管道,可保证有充足的时间将这前q个装满。

  • 若开启前q个管道而在 d p [ n ] dp[n] dp[n]的时间内所有水库已经装满,则q一定满足条件。且一定有 t q u e r y ⋅ q ≥ d p [ n ] ⋅ q ≥ s u m ( v [ 1 : n ] ) t_{query} \cdot q \geq dp[n] \cdot q\geq sum(v[1:n]) tqueryqdp[n]qsum(v[1:n])
  • 否则可看作前q的管道同时注水,速率混合。因此需要保证在 t q u e r y t_{query} tquery的时间内,以q速率,能将水库注满,也有: t q u e r y ⋅ q ≥ s u m ( v [ 1 : n ] ) t_{query} \cdot q \geq sum(v[1:n]) tqueryqsum(v[1:n])

因此给定q,只需检查
t q u e r y ⋅ q ≥ s u m ( v [ 1 : n ] ) t_{query} \cdot q \geq sum(v[1:n]) tqueryqsum(v[1:n])是否满足即可。找到最小这样的q即可。

#include <bits/stdc++.h>
using namespace std;
#include<stack>
#define int long long
signed main()
{
    int n;
    cin >> n;
    int v[n];
    int prev[n + 1];
    prev[0] = 0;
    for(int i = 0;i<n;i++) 
    {
        scanf("%ld",&v[i]);
        prev[i + 1]  = prev[i] + v[i];
    }
    int dp[n + 1];
    dp[1] = v[0];
    for(int i = 2;i<=n;i++) dp[i] = max(dp[i - 1],(long long)ceil((double)prev[i] / i));
    int q;
    cin >> q;
    for(int i = 0;i<q;i++)
    {
        int t;
        scanf("%ld",&t);
        if(t < dp[n]) printf("-1\n");
        else   //找到第一个大于等于ceil(prev[n]/t)的prefixsum之和
        {
            t = (int)ceil((double)prev[n]/t);
            printf("%ld\n",t);
        }
    }
    system("pause");
}

E题-Serega the Pirate在这里插入图片描述

在这里插入图片描述

  • a=0的充要条件是除1外,每格点四周至少一个比其小的格点。称满足条件的格点为好格点,否则为坏格点
  • 坏格点的常数上界: 交换1次最多改变10个格点的好坏性,因此坏格点多于10个一定至少需要2次交换
  • 坏格点数小于等于10时,对每个坏格点及其四周格点,将其与mn个格点分别交换。可在 O ( 1 ) O(1) O(1)判断格局变换后的坏格点数。

代码老是T待查明原因:

#include<bits/stdc++.h>
using namespace std;

bool judge(int **a,int i,int j,int n,int m)
{
    bool f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j];
    bool f2 = i + 1 < n && a[i + 1][j] < a[i][j];
    bool f3 = j + 1 < m && a[i][j + 1] < a[i][j];
    bool f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j];
    return !(f1 || f2 || f3 || f4 || a[i][j] == 1);
}
bool update(int **a,bool **is,int n,int m,int i1,int j1,int i2,int j2,int ori,vector<array<int,2>>& con)
{
    //判断a数组的(i_1,j_1)和(i_2,j_2)交换后坏点数的变化情况
    int t = a[i1][j1];
    a[i1][j1] = a[i2][j2];
    a[i2][j2] = t;
    set<pair<int,int>> c;
    c.insert(make_pair(i1,j1)); c.insert(make_pair(i2,j2));
    for(auto& p : con)
    {
        if(i1 + p[0] >=0 && i1 + p[0]<n && j1 + p[1]>=0 && j1 + p[1]<m) c.insert(make_pair(i1+p[0],j1+p[1]));
        if(i2 + p[0] >=0 && i2 + p[0]<n && j2 + p[1]>=0 && j2 + p[1]<m) c.insert(make_pair(i2+p[0],j2+p[1]));
    }
    for(auto&p : c)
    {
        int x = p.first; int y = p.second;
        if(is[x][y]==true && judge(a,x,y,n,m)==false)  ori--;
        if(is[x][y]==false && judge(a,x,y,n,m) == true) ori++;
    }
    t = a[i1][j1];
    a[i1][j1] = a[i2][j2];
    a[i2][j2] = t;
    return ori == 0;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int **a = new int *[n];
    for(int i = 0;i<n;i++) a[i] = new int[m];
    bool **is = new bool* [n];
    for(int i = 0;i<n;i++) is[i] = new bool[m];
    for(int i = 0;i<n;i++) for(int j = 0;j<m;j++) {is[i][j]=false;scanf("%d",&a[i][j]);}
    int v = 0;
    vector<array<int,2>> b;
    vector<array<int,2>> con = {{1,0},{-1,0},{0,1},{0,-1}};
    for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
    {
        bool f1 = false,f2 = false,f3 = false,f4 = false;
        f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j];
        f2 = i + 1 < n && a[i + 1][j] < a[i][j];
        f3 = j + 1 < m && a[i][j + 1] < a[i][j];
        f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j];
        if(f1 || f2 || f3 || f4 || a[i][j] == 1) {is[i][j]=false;} else {b.push_back({i,j});is[i][j]=true;};
    }
    v = b.size();
    if(v == 0) printf("0\n");
    else if(v >= 11) printf("2\n");
    else{   //v<=10,是否能两次交换成功
        int cont = 0;
        set<pair<int,int>> f;
        for(auto& p : b)
        {
            int x = p[0]; int y = p[1];
            for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
            {
                if(x == i && j == y) continue;
                else if(update(a,is,n,m,i,j,x,y,v,con))
                {
                   f.insert(make_pair(a[i][j],a[x][y]));
                   f.insert(make_pair(a[x][y],a[i][j]));
                }
            }
            for(auto& q:con)
            {
                int x = p[0] + q[0]; int y = p[1] + q[1];
                if(x >= 0 && x < n && y >= 0 && y < m)
                {
                    for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
                    {
                        if(x == i && j == y) continue;
                        else if(update(a,is,n,m,i,j,x,y,v,con))
                        {
                            f.insert(make_pair(a[i][j],a[x][y]));
                            f.insert(make_pair(a[x][y],a[i][j]));
                        }
                     }
                }
            }
        }
        if(f.size() > 0) printf("1 %d\n",f.size()/2);
        else printf("2\n");
    }
    system("pause");
    return 0; 
}

可能原因:update中set频繁使用。
改变方式:交换后检查原先所有坏点(最多10个)和交换后可能改变好坏的点(最多10个)。不会怎么引入常数。

#include<bits/stdc++.h>
using namespace std;

inline bool judge(int **a,int i,int j,int n,int m)
{
    if(i < 0 || i >= n || j <0 || j >= m) return false;
    if(a[i][j]==1) return false;
    bool f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j]; if(f1) return false;
    bool f2 = i + 1 < n && a[i + 1][j] < a[i][j];  if(f2) return false;
    bool f3 = j + 1 < m && a[i][j + 1] < a[i][j];  if(f3) return false;
    bool f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j]; if(f4) return false;
    return true;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int **a = new int *[n];
    for(int i = 0;i<n;i++) a[i] = new int[m];
    for(int i = 0;i<n;i++) for(int j = 0;j<m;j++) {scanf("%d",&a[i][j]);}
    int v = 0;
    vector<array<int,2>> b;
    vector<array<int,2>> con = {{1,0},{-1,0},{0,1},{0,-1},{0,0}};
    for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
    {
        bool f1 = false,f2 = false,f3 = false,f4 = false;
        f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j];
        f2 = i + 1 < n && a[i + 1][j] < a[i][j];
        f3 = j + 1 < m && a[i][j + 1] < a[i][j];
        f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j];
        if(f1 || f2 || f3 || f4 || a[i][j] == 1) {} else {b.push_back({i,j});}
    }
    v = b.size();
    if(v == 0) printf("0\n");
    else if(v >= 11) printf("2\n");
    else{   //v<=10,是否能两次交换成功
        int cont = 0;
        set<pair<int,int>> f;
        for(auto& p : b)
        {
            for(auto& q:con)
            {
                int x = p[0] + q[0]; int y = p[1] + q[1];
                if(x >= 0 && x < n && y >= 0 && y < m)
                {
                    
                    for(int i = 0;i<n;i++)
                        for(int j = 0;j<m;j++)
                        {
                            bool flag = true;
                            swap(a[i][j],a[x][y]);
                            // if(min(a[i][j],a[x][y]) == 2 && max(a[i][j],a[x][y]) == 4) 
                            // printf("h");
                            //检查原先所有坏点
                            for(auto & o:b) if(judge(a,o[0],o[1],n,m)) {flag = false;}
                            //检查可能改变好坏性的点
                            for(auto & o:con)
                            {
                                int ix = o[0] + i; int iy = o[1] + j;
                                if(judge(a,ix,iy,n,m)) {flag = false;}
                                ix = o[0] + x; iy = o[1] + y;
                                if(judge(a,ix,iy,n,m)) {flag = false;}
                            }
                            if(flag)
                            {
                                f.insert({min(a[i][j],a[x][y]),max(a[i][j],a[x][y])});
                            }
                            swap(a[i][j],a[x][y]);
                        }

                }
            }
        }
        //for(auto& p:f) cout << p.first << " " << p.second << endl;
        if(f.size() > 0) printf("1 %d\n",f.size());
        else printf("2\n");
    }
    system("pause");
    return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值