Codeforces Round #759 (Div. 2)

A. Find Array

题意:

有一朵花初始1厘米高,给出n天是否浇水的情况。要求求出最后花朵的高度

如果花朵死亡,则返回-1

  • 连续两天不浇水死亡
  • 今天浇水昨天没浇水张高1厘米
  • 今天浇水了昨天也浇水了张高5厘米
  • 今天没浇水,不长高

分析:

按照题意模拟即可

代码:

#include
using namespace std;

int main()
{
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while (T--)
    {
        int n;cin>>n;
        int pre = -1;
        int cur = 1;
        int die = 0;
        for (int i=1;i<=n;++i)
        {
            int sta;cin>>sta;
            if (sta==0&&pre==0)
                die = 1;
            else if (sta==1&&pre==1)
                cur+=5;
            else if (sta==1)
                cur+=1;
            pre = sta;
        }
        if (die)cout<<"-1\n";
        else cout<

B. Array Eversion

题意:

给一个长为n的数组a

定义转置操作:

选取数字 a n a_n an,将数组a中所有 ≤ a n \le a_n an的数字放在左边

所有 ≥ a n \ge a_n an的数字放在右边

两边数字与数字之间原本的顺序保持不变

换句话说,转置操作是稳定的

要求你计算,在第几次之后的转置操作不会再改变数组a

分析:

如果 a n a_n an为最大值的话,那就不可能改变数组a了

因此,我们要计算过了几次之后 a n a_n an会变成最大值

按照稳定的排序的想法,进行一次转置操作之后,新的 a n a_n an是距离原本 a n a_n an最近的

第一个大于他的值


代码:

#include
using namespace std;
const int maxn = 2e5+100;
int a[maxn];
int n;
int main()
{
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while (T--)
    {
        cin>>n;
        for (int i=1;i<=n;++i)cin>>a[i];
        int cur = a[n];
        int ans = 0;
        for (int i=n-1;i>=1;--i)if (a[i]>cur)
        {
            ++ans;
            cur = a[i];
        }cout<

C. Minimize Distance

题意:

数轴上,坐标0处有 n n n个货物

坐标轴上有 n n n个仓库,你要给每个仓库分配一个货物

刚开始你在坐标0上,你一次最多可以拿 k k k个货物,请问你最短需要走多远的路?

不要求最后返回坐标0

分析:

先不考虑负轴。

对于仓库 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5

k = 2 k=2 k=2

一个贪心的想法是,我们先填 1 , 2 1,2 1,2再填 3 , 4 3,4 3,4,然后最后单独填 5 5 5

这样的花费为: 2 × 2 + 2 × 4 + 5 = 17 2\times 2+2\times 4+5=17 2×2+2×4+5=17

很明显,有更好的策略

我们可以:先填 1 1 1,再填 2 , 3 2,3 2,3,最后填 4 , 5 4,5 4,5

花费为: 2 × 1 + 2 × 3 + 5 = 13 2\times 1+2\times 3+5=13 2×1+2×3+5=13


分别单独对正轴和负轴实行上述策略即可。

然后因为,我们最后选择是呆在最左边还是最右边取决于两边绝对值的大小


代码:

#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int a[maxn];
int n,k;
int main()
{
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while (T--)
    {
        cin>>n>>k;
        for (int i=1;i<=n;++i)cin>>a[i];
        sort(a+1,a+1+n);
        ll ans = 0;
        for (int i=1;i<=n&&a[i]<0;i+=k)
            ans += 2*abs(a[i]);
        for (int i=n;i>0&&a[i]>0;i-=k)
            ans += 2*a[i];
        ans -= max(abs(a[1]), abs(a[n]));
        cout<

D. Yet Another Sorting Problem

题意:

给你个长为n的数组,你之可以进行 3 − c y c l e 3-cycle 3cycle变换,问是否可以实现排序

3 − c y c l e 3-cycle 3cycle的定义是:

选取 i , j , k i,j,k i,j,k互不相等,然后让 a [ i ] → a [ j ] → a [ k ] → a [ i ] a[i]\rightarrow a[j]\rightarrow a[k]\rightarrow a[i] a[i]a[j]a[k]a[i]

分析:

拿到现有的数组之后a

我们得到一个排序后的数组b

按照b我们可以得知a数组中的每一个数 a i a_i ai应该到哪一个索引 j j j

因此,可以建成一张图

也就是,一个个环。


分奇偶环考虑

考虑奇环,对于长度为 1 , 3 1,3 1,3的奇环,显然可以排序成功

对于长度为 5 5 5的奇环,例如数组: 2 , 3 , 4 , 5 , 1 2,3,4,5,1 2,3,4,5,1

环为: 1 → 2 → 3 → 4 → 5 → 1 1\rightarrow 2\rightarrow 3\rightarrow 4\rightarrow 5\rightarrow 1 123451

假设我们选取 i = 3 , j = 4 , k = 5 i=3,j=4,k=5 i=3,j=4,k=5

那么,数组变为: 2 , 3 , 1 , 4 , 5 2,3,1,4,5 2,3,1,4,5

只剩下一个长为 3 3 3的奇环。

也就是说,对于任意的奇环,我们都可以 − 2 , − 2 , … , − 2 -2,-2,\dots,-2 2,2,,2地使得他们变成长度只有 3 3 3的奇环,从而实现排序

偶环的情况下:

观察所给样例:

2 , 1 , 4 , 3 2,1,4,3 2,1,4,3

这里两个偶环的情况下是可以实现排序的

原因是,两个偶环可以一次 3 − c y c l e 3-cycle 3cycle操作变成一个奇环

因此,如果有偶环则一定要使得偶环的数目是偶数


而如果数组a中有重复的元素的话,我们划分的偶数环的个数是不一样的

我们可以通过重复元素,来调节偶数环的个数,从而导致一定可以排序成功!


代码:

#include
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
int G[maxn];
int a[maxn];
int vis[maxn];
int n;
int main()
{
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while (T--)
    {
        cin>>n;
        for (int i=1;i<=n;++i)vis[i]=G[i]=0;
        bool f = false;
        for (int i=1;i<=n;++i)
        {
            cin>>a[i];
            if (G[a[i]]!=0)f=true;
            G[a[i]]=i;
        }
        if (n==1)
        {
            cout<<"YES\n";
            continue;
        }else if(n==2)
        {
            if (a[1]<=a[2])cout<<"YES\n";
            else cout<<"NO\n";
            continue;
        }
        if (f)
        {
            cout<<"YES\n";
            continue;
        }
        sort(a+1,a+1+n);
        int cnt = 0;
        for (int i=1;i<=n;++i)if (!vis[i])
        {
            int cur = i;
            int len = 0;
            while (true)
            {
                vis[cur]=1;
                ++len;
                cur = G[a[cur]];
                if (vis[cur])break;
            }
            if (len%2==0)++cnt;
        }
        if (cnt&1)cout<<"NO\n";
        else cout<<"YES\n";
    }
}

E.Frequency Queries

题意:

给一颗n个节点的树,每个点的点权在 1 1 1 n n n之间

q q q次查询,每次查询格式如下:

v , l , k v,l,k v,l,k

要求返回:

从点 v v v到根节点的最短路径上,出现次数大于 l l l次的点权中,按照出现此书排序的

k k k多的点权值,如果有并列情况则返回任意即可。

分析:

我们可以离线解决这个问题

先记录出每个点上的查询。

然后从根节点开始 d f s dfs dfs,在 d f s dfs dfs的过程中维护线段树,利用线段树实现查询

每次 d f s dfs dfs到一个新的节点时,我们线段树对这个点权出现次数加一

然后利用线段树查询这个点上所有的询问

之后回溯的过程时,我们再在线段树上删去这个点权

代码:

#include
using namespace std;
const int N=1e6+10;
setst[N],exi;
struct que{
    int l,k,num;
};
vectorv[N];
int t,n,q,ans[N],val[N],tim[N];
struct edge{
    int to,nex;
};
edge ed[N<<1];
int cnt,h[N];
void add(int st,int et){
    cnt++;
    ed[cnt].to=et;
    ed[cnt].nex=h[st];
    h[st]=cnt;
}
int sum[N<<2];
void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void upd(int rt,int l,int r,int L,int R,int d)
{
    if(L<=l && r<=R)
    {
        sum[rt]+=(r-l+1)*d;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) upd(rt<<1,l,mid,L,R,d);
    if(R>mid) upd(rt<<1|1,mid+1,r,L,R,d);
    pushup(rt);
}
int query(int rt,int l,int r,int L,int R)
{
    if(L<=l && r<=R)
    {
        return sum[rt];
    }
    int mid=(l+r)>>1,res=0;
    if(L<=mid) res += query(rt<<1,l,mid,L,R);
    if(R>mid) res += query(rt<<1|1,mid+1,r,L,R);
    return res;
}
int getval(int rt,int l,int r,int now){
    if(l==r)return l;
    int mid=(l+r)>>1;
    if(sum[rt<<1]

你可能感兴趣的:(codeforces题解,算法,c++,编程语言,acm竞赛)