3.25训练题

A

You're given two arrays a[1…n] and b[1…n], both of the same length n.

In order to perform a push operation, you have to choose three integers l,r,k satisfying 1≤l≤r≤n and k>0. Then, you will add k to 
elements al,al+1,…,ar. For example, if a=[3,7,1,4,1,2] and you choose (l=3,r=5,k=2), the array a will become [3,7,3,6,3−−−−−,2]. You can do this operation at most once. Can you make array a equal to array b? (We consider that a=b if and only if, for every 1≤i≤n, ai=bi)

这道题罚时26分钟,还wa了一发嘤嘤嘤,按照lbt学长的说法,A题应该几分钟就完事,窝太弱了。题意很简单,就是在a里找出一段整体加一个正数使得ab序列完全一样,也就是ab只有连续的一段差值相等并且是正数的时候才能符合要求。我一开始写的有点恶心,先找l和k又找r判断半天,其实我们只需要两头找l和r,然后判断中间的是不是差值都一样就可以了

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn=1e5+5;
 5 int t,n;
 6 int a[maxn],b[maxn];
 7 int main()
 8 {
 9     scanf("%d",&t);
10     while(t--)
11     {
12         scanf("%d",&n);
13         for(int i=1;i<=n;++i)scanf("%d",&a[i]);
14         for(int i=1;i<=n;++i)scanf("%d",&b[i]);
15         int l=0,r=0,k=0;
16         bool flag=1;
17         for(int i=1;i<=n;++i)
18         {
19             if(a[i]!=b[i])
20             {
21                 l=i;break;
22             }
23         }
24         for(int i=n;i>=1;i--)
25         {
26             if(a[i]!=b[i])
27             {
28                 r=i;break;
29             }
30         }
31         k=b[l]-a[l];
32         for(int i=l;i<=r;++i)
33             if(a[i]+k!=b[i])
34             {
35                 flag=0;break;
36             }
37         if(!flag||k<0)printf("NO
");
38         else printf("YES
");
39     }
40     return 0;
41 } 

B

The Central Company has an office with a sophisticated security system. There are 106 employees, numbered from 1 to 106.

The security system logs entrances and departures. The entrance of the i-th employee is denoted by the integer i, while the departure of the i-th employee is denoted by the integer −i.

The company has some strict rules about access to its office:

An employee can enter the office at most once per day.
He obviously can't leave the office if he didn't enter it earlier that day.
In the beginning and at the end of every day, the office is empty (employees can't stay at night). It may also be empty at any moment of the day.
Any array of events satisfying these conditions is called a valid day.

Some examples of valid or invalid days:

[1,7,−7,3,−1,−3] is a valid day (1 enters, 7 enters, 7 leaves, 3 enters, 1 leaves, 3 leaves).
[2,−2,3,−3] is also a valid day.
[2,5,−5,5,−5,−2] is not a valid day, because 5 entered the office twice during the same day.
[−4,4] is not a valid day, because 4 left the office without being in it.
[4] is not a valid day, because 4 entered the office and didn't leave it before the end of the day.
There are n events a1,a2,…,an, in the order they occurred. This array corresponds to one or more consecutive days. 
The system administrator erased the dates of events by mistake, but he didn't change the order of the events. You must partition (to cut) the array a of events into contiguous subarrays, which must represent non-empty valid days (or say that it's impossible).
Each array element should belong to exactly one contiguous subarray of a partition. Each contiguous subarray of a partition should be a valid day.
For example, if n=8 and a=[1,−1,1,2,−1,−2,3,−3] then he can partition it into two contiguous subarrays which are valid days: a=[1,−1 | 1,2,−1,−2,3,−3]. Help the administrator to partition the given array a in the required way or report that it is impossible to do. Find any required partition,
you should not minimize or maximize the number of parts.

题面略长让一些人直接放弃了,但其实不难,就是有一个序列,分成若干份,每一份里一个数可以先进站然后出站,但不能先出后进也不能重复进,最后站必须为空,问能不能划分出符合要求的序列。我们开一个cnt数组记录当前这一个序列有多少个人在站里,当cnt为零0时立马结束这一词的序列这样可以保证最大限度无重复进。vis数组记录从未进过站还是在站里还是进去又出去了,需要注意的是vis数组是1e6每次memset会t,所以我们只需要把这一序列里面的数清空就可以了,其他的这一词没有涉及,也不会改变她的状态

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 using namespace std;
 6 const int maxn=1e5+5;
 7 int n;
 8 int a[maxn];
 9 int cnt;
10 int ans[maxn];
11 int vis[maxn*10];
12 bool flag=1;
13 int k;
14 int main()
15 {
16     scanf("%d",&n);
17     for(int i=1;i<=n;++i)
18     {
19         scanf("%d",&a[i]);
20     }
21     int tmp=0;
22     for(int i=1;i<=n;++i)
23     {
24         tmp++;
25         if(a[i]<0)
26         {
27             k=-a[i];
28             if(!vis[k]||vis[k]==2)
29             {
30                 flag=0;break;
31             }else
32             vis[k]=2,cnt--;
33         }else
34         {
35             k=a[i];
36             if(vis[k])
37             {
38                 flag=0;break;
39             }else
40             {
41                 vis[k]=1;cnt++;
42             }
43         }
44         if(cnt==0)
45         {
46             ans[++ans[0]]=tmp;
47             for(int j=i-tmp;j<=i;++j)vis[abs(a[j])]=0;
48             tmp=0;
49         }
50     }
51     if(!flag||cnt)printf("-1
");
52     else{
53         printf("%d
",ans[0]);
54         for(int i=1;i<=ans[0];++i)
55             cout<<ans[i]<<" ";
56     }
57     return 0;
58 }
Tsumugi brought n delicious sweets to the Light Music Club. They are numbered from 1 to n, where the i-th sweet has a sugar concentration described by an integer ai.

Yui loves sweets, but she can eat at most m sweets each day for health reasons.

Days are 1-indexed (numbered 1,2,3,…). Eating the sweet i at the d-th day will cause a sugar penalty of (d⋅ai), as sweets become more sugary with time. A sweet can be eaten at most once.

The total sugar penalty will be the sum of the individual penalties of each sweet eaten.

Suppose that Yui chooses exactly k sweets, and eats them in any order she wants. What is the minimum total sugar penalty she can get?

Since Yui is an undecided girl, she wants you to answer this question for every value of k between 1 and n.

首先一看就是贪心,不过要证明的话也不是没有根据,我们都知道正序不等式>=乱序不等式>=倒序不等式,所以我们第一天吃甜度最大的,往后依次顺延,但是每天最多可以吃m个该怎么求呐,先简化一下,假如每天都是吃正好m个,第n天的时候答案是f[n],那么第n+1天的时候我们需要把最大的m个放到第一天,次大的m个放到第2天,依次往后推,每m个往后推一天,产生的值是s[(n+1)*m](前缀和),而在第n天的基础上加上这个数就是第n+1天的答案,但现在我们要求的是没x个甜品产生的最小甜度,那也是把第n天的往后移一天,所以f[x]=f[x-m]就可以了

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstdio>
 4 using namespace std;
 5 const int maxn=2e5+5;
 6 int n,m;
 7 int a[maxn];
 8 long long ans[maxn];
 9 long long s[maxn];
10 int main()
11 {
12     scanf("%d%d",&n,&m);
13     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
14     sort(a+1,a+n+1);
15     for(int i=1;i<=n;i++)
16     {
17         s[i]=s[i-1]+a[i];
18     }
19     for(int i=1;i<=n;++i)
20     {
21         if(i>m)ans[i]=ans[i-m]+s[i];
22         else ans[i]=s[i];
23     }
24     for(int i=1;i<=n;++i)
25         printf("%lld ",ans[i]);
26     return 0;
27 }
You're given an undirected graph with n nodes and m edges. Nodes are numbered from 1 to n.

The graph is considered harmonious if and only if the following property holds:

For every triple of integers (l,m,r) such that 1≤l<m<r≤n, if there exists a path going from node l to node r, 
then there exists a path going from node l to node m. In other words, in a harmonious graph, if from a node l we can reach a node r through edges (l<r),
then we should able to reach nodes (l+1),(l+2),…,(r−1) too. What is the minimum number of edges we need to add to make the graph harmonious?

题面很简单,如果数x和数y相连,并且y>x,那么需要x和x+1至y-1的所有数相连,显然是并查集,但是暴力并查集会t,这里借鉴了别人的做法,具体在代码里解释

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=2e5+5;
int n,m;
int x,y;
int fa[maxn];
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
} 
int ans;
struct zhw{
    int Min,Max;
    friend bool operator <(const zhw a,const zhw b)
    {
        if(a.Min==b.Min)return a.Max<b.Max;
        return a.Min<b.Min;
    }
}a[maxn];
int mi[maxn],mx[maxn];
int cnt;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)fa[i]=i;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        x=find(x),y=find(y);
        if(x!=y)
        {
            int fx=max(x,y),fy=min(x,y);//把开始的图相连的点连接起来
            fa[fy]=fx; 
        }
    }
    memset(mi,127/3,sizeof(mi));    
    for(int i=1;i<=n;++i)
    {
        int fx=find(i);
        mi[fx]=min(mi[fx],i),mx[fx]=max(mx[fx],i); //找到每一块图的最大点和最小点
    }
    for(int i=1;i<=n;++i)
        if(fa[i]==i)a[++cnt].Min=mi[i],a[cnt].Max=mx[i];//记录下来每一块图的最大点和最小点
    sort(a+1,a+cnt+1);//排序,按照每一块的最小值从小到大排排,最小值一样的按照最大值从小到大排
    int fi=a[1].Min,fm=a[1].Max;
    for(int i=2;i<=cnt;++i)
    {
        if(a[i].Min<fm)//如果一块的最小值小于另一块的最大值,他的最大值要么小于另一块的最大值,要么大于另一块的最大值,那么他们一定是交叉或者包含的关系
        {//无论是哪一种这两块都需要连接起来
            ans++,fm=max(fm,a[i].Max);//这一块的长度最大值扩增,不需要再合并这两块了,fm更新就是合并他们之后的目的了
        }else
        {
            fi=a[i].Min,fm=a[i].Max;//否则就是前面一块完整地连接完了,找下一块就可以了
        }
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/yuelian/p/12573668.html