ACM寒假集训Day2-基础数据结构


这天的集训题目里面涉及到的基础数据结构其实大多数都是stl的东西,比如queue,map,set等等。在写题解的时候就顺带把这些东西复习一下好了。

 


 

A - Table Tennis

n people are standing in a line to play table tennis. At first, the first two players in the line play a game. Then the loser goes to the end of the line, and the winner plays with the next person from the line, and so on. They play until someone wins k games in a row. This player becomes the winner.

For each of the participants, you know the power to play table tennis, and for all players these values are different. In a game the player with greater power always wins. Determine who will be the winner.

Input

The first line contains two integers: n and k (2 ≤ n ≤ 500, 2 ≤ k ≤ 1012) — the number of people and the number of wins.

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ n) — powers of the player. It's guaranteed that this line contains a valid permutation, i.e. all ai are distinct.

Output

Output a single integer — power of the winner.

Input
2 2
1 2
Output
2 
Input
4 2
3 1 2 4
Output
3 
Input
6 2
6 5 3 1 2 4
Output
6 
Input
2 10000000000
2 1
Output
2

这道题我先是用队列模拟比赛,直到有人赢了k次,这个就瞬间T了,看最后一个样例数据,k是1e10,按1s1亿次计算也要10秒,难顶,所以不可能枚举所有的比赛情况。后来在同学的指点下找到了答案:只需要模拟n次。因为比赛n次以后,每个人都参加了至少一次比赛,而且power值最大的那个人一定在最前面,如果n比完以后还没有人赢,那么赢家一定是最前面的人,如果在这个过程中有人赢了k次,就直接结束并打印就好了。在这个过程中我又犯了一个致命错误,害我一直wa在test 11,我是边输入power边进行比赛模拟,赢了k次就让result等于这个人的power。这样的话后面也有可能有个人赢了k次把result更新了,所以会wa。于是我先一次性输入好,再用队列模拟就没问题了。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 using namespace std;
 5 struct node
 6 {
 7     int power;
 8     int wins;//记录胜利的次数
 9 }a[501];
10 int main()
11 {
12     queue<struct node>q;
13     int n,result=0;
14     long long k=0;
15     while(cin>>n>>k)
16     {
17       while(!q.empty())q.pop();//每次开始前要清空队列
18        result=0;
19        for(int i=1;i<=n;i++)cin>>a[i].power;//一次性全部输入
20        a[1].wins=0;
21         q.push(a[1]);//先把第一个人push进去,以便后面比较
22         for(int i=2;i<=n;i++)
23         {
24           if(q.front().power>a[i].power)//如果比第一个人弱,就push到队尾
25           {
26             q.front().wins++;
27             a[i].wins=0;
28             q.push(a[i]);
29           }
30           if(q.front().wins>=k)//已经赢了k次,直接结束比赛
31           {
32             result=q.front().power;
33             break;
34           }
35           if(q.front().power<a[i].power)//否则把第一个人push到队尾,队头换成赢者
36           {
37             a[i].wins=1;
38             q.push(q.front());
39             q.front()=a[i];
40           }
41         }
42         if(result==0)result=q.front().power;
43         cout<<result<<endl;
44         //cout<<q.front().power<<endl;
45     }
46 }

 


 B - 语法检查-括号匹配

欢迎大家加入ACM!
要深入的学习ACM的相关知识,首先就必须学会一门编程语言,推荐C/C++。
不过对于初学者,因为没编过多少代码,经常出现变异错误,其实就是语法错误。
现在要你检查一段代码的语法是否否正确。
为了简化问题,我们只检查 (、)、{、} 括号是否匹配了,并且在输入的代码中不包含字符的'(',')','{','}'。
其他语法错误不检查,如 "#include <stdio.h","2 = x",是错误的,但是不检查。

Input

有多组测试数据,每组测试数据有一段代码,该段代码可能在有多行。
每段代码以Ctrl+Z结束。
处理到文件结束。

Output

每组测试数据输出一行。
如果这段代码括号匹配了,输出 Right ,否则输出 Wrong。

Sample Input

#include <stdio.h
int main(){
    int a b;
    while (scanf("%d%d", &a, &b) != EOF) {
        printf("%d\n", a + b);
    }
}
Ctrl+Z
int main(){
    int a, b;
    while (scanf("%d%d", &a, &b) != EOF) {
        printf("%d\n", a + b);
   
}
Ctrl+Z

Sample Output

Right
Wrong


实不相瞒,这道题我虽然a了,但我用的是错误的做法——回文数。我把所有括号按顺序放入数组,然后判断是否是回文数。如果有两个应该匹配的括号刚好是反转的,那我的结论就错了。害,还是贴下代码把:

#include<iostream>
#include<string>
using namespace std;

string a;
string b;
int main()
{
     int c=0;
    while(cin>>a)
    {
        for(int i=0;i<a.size();i++)
        {
           if(a[i]=='('||a[i]==')'||a[i]=='{'||a[i]=='}')
            {
                b[c]=a[i];c++;
            }
        }
        //cout<<c<<endl;
         if(a=="Ctrl+Z")
        {
            if(c%2==1)cout<<"Wrong"<<endl;
            else
            {
                int s=0;
                for(int i=0;i<c/2;i++)if(b[i]=b[c-i-1])s++;
                if(s==c/2)cout<<"Right"<<endl;
                else cout<<"Wrong";
            }
            c=0;
        }
    }

}

 


 C - The kth great number

Xiao Ming and Xiao Bao are playing a simple Numbers game. In a round Xiao Ming can choose to write down a number, or ask Xiao Bao what the kth great number is. Because the number written by Xiao Ming is too much, Xiao Bao is feeling giddy. Now, try to help Xiao Bao.

Input

There are several test cases. For each test case, the first line of input contains two positive integer n, k. Then n lines follow. If Xiao Ming choose to write down a number, there will be an " I" followed by a number that Xiao Ming will write down. If Xiao Ming choose to ask Xiao Bao, there will be a "Q", then you need to output the kth great number.
Output

The output consists of one integer representing the largest number of islands that all lie on one line.
Sample Input

8 3
I 1
I 2
I 3
Q
I 5
Q
I 4
Q

Sample Output

1
2
3

这道题我一来就暴力,用数组存储输入的数,每次查询都sort一遍再打印第k大的数,一下就T了。正确思路应该是维护一个含k个数的从小到大的队列,每次查询只需要访问队首即可。这就要用到优先队列了,不过优先队列priority_queue<int>是降序的,要用升序的版本:priority_queue<int,vector<int>,greater<int>>;

详见代码:

 1 #include<iostream>
 2 #include<queue>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     priority_queue<int,vector<int>,greater<int>>a;//定义一个升序的优先队列
 8     int n,k,t,c=0;
 9     while(cin>>n>>k)
10     {
11     char s;
12     for(int i=0;i<n;i++)
13     {
14         cin>>s;
15         if(s=='I')
16         {
17             cin>>t;
18             a.push(t);
19             if(a.size()>k)a.pop();//队列长度大于k,则把队首去掉以保证当前队首是第k大的数
20         }
21         if(s=='Q')cout<<a.top()<<endl;
22     }
23     while(!a.empty())a.pop();//每个样例之后记得清空队列
24     }
25 }

 


 

D - Sonya and Queries

Today Sonya learned about long integers and invited all her friends to share the fun. Sonya has an initially empty multiset with integers. Friends give her t queries, each of one of the following type:

  1.  +  ai — add non-negative integer ai to the multiset. Note, that she has a multiset, thus there may be many occurrences of the same integer.
  2.  -  ai — delete a single occurrence of non-negative integer ai from the multiset. It's guaranteed, that there is at least one ai in the multiset.
  3. s — count the number of integers in the multiset (with repetitions) that match some pattern s consisting of 0 and 1. In the pattern, 0 stands for the even digits, while 1 stands for the odd. Integer x matches the pattern s, if the parity of the i-th from the right digit in decimal notation matches the i-th from the right digit of the pattern. If the pattern is shorter than this integer, it's supplemented with 0-s from the left. Similarly, if the integer is shorter than the pattern its decimal notation is supplemented with the 0-s from the left.

For example, if the pattern is s = 010, than integers 92, 2212, 50 and 414 match the pattern, while integers 3, 110, 25 and 1030 do not.

Input

The first line of the input contains an integer t (1 ≤ t ≤ 100 000) — the number of operation Sonya has to perform.

Next t lines provide the descriptions of the queries in order they appear in the input file. The i-th row starts with a character ci — the type of the corresponding operation. If ci is equal to '+' or '-' then it's followed by a space and an integer ai (0 ≤ ai < 1018) given without leading zeroes (unless it's 0). If ci equals '?' then it's followed by a space and a sequence of zeroes and onse, giving the pattern of length no more than 18.

It's guaranteed that there will be at least one query of type '?'.

It's guaranteed that any time some integer is removed from the multiset, there will be at least one occurrence of this integer in it.

Output

For each query of the third type print the number of integers matching the given pattern. Each integer is counted as many times, as it appears in the multiset at this moment of time.

Examples

Input
12
+ 1
+ 241
? 1
+ 361
- 241
? 0101
+ 101
? 101
- 101
? 101
+ 4000
? 0
Output
2
1
2
1
1
Input
4
+ 200
+ 200
- 200
? 0
Output
1

 


这道题在匹配奇偶的时候可以有很多想法,暴力比对(每一位数%2后直接和?后面的数匹配)我试过,wa在test4两次,实在找不到bug就难受的要死,然后借鉴网上的做法,每一位数都%2以后不久只剩下0和1了吗?把这个数看成是二进制的,然后再换成十进制的,因为每一个二进制数唯一对应一个十进制数(废话),这就可以用数组把每个数字对应的那个十进制数出现的个数存储起来,然后?出现就打印下标为这个数的数组的值就行了。

代码短的可怕:

 1 #include <bits/stdc++.h>
 2 #include<string>
 3 using namespace std;
 4 int cnt[1<<18A];
 5 int main()
 6 {
 7     int t;
 8     scanf("%d",&t);
 9     while(t--)
10     {
11         char t;
12         string a;
13         cin>>t>>a;
14         int tmp=0;
15         for(int i=0;a[i];i++)
16             tmp=tmp*2+(a[i]-'0')%2;
17         if(t=='?')printf("%d\n",cnt[tmp]);
18         else cnt[tmp]+=(t=='+' ? 1 : -1);
19     }
20     return 0;
21 }

 

 


 

E - A and B and Interesting Substrings

 

A and B are preparing themselves for programming contests.

After several years of doing sports programming and solving many problems that require calculating all sorts of abstract objects, A and B also developed rather peculiar tastes.

A likes lowercase letters of the Latin alphabet. He has assigned to each letter a number that shows how much he likes that letter (he has assigned negative numbers to the letters he dislikes).

B likes substrings. He especially likes the ones that start and end with the same letter (their length must exceed one).

Also, A and B have a string s. Now they are trying to find out how many substrings t of a string s are interesting to B (that is, t starts and ends with the same letter and its length is larger than one), and also the sum of values of all letters (assigned by A), except for the first and the last one is equal to zero.

Naturally, A and B have quickly found the number of substrings t that are interesting to them. Can you do it?

Input

The first line contains 26 integers xa, xb, ..., xz ( - 105 ≤ xi ≤ 105) — the value assigned to letters a, b, c, ..., z respectively.

The second line contains string s of length between 1 and 105 characters, consisting of Lating lowercase letters— the string for which you need to calculate the answer.

Output

Print the answer to the problem.

Examples

Input
1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 7 1 1 1 8 1 1 1 1 1 1
xabcab
Output
2
Input
1 1 -1 1 1 1 1 1 1 1 1 1 1 1 1 7 1 1 1 8 1 1 1 1 1 1
aaa
Output
2

这道题要用前缀和+map来实现。用一个sum来记录前缀和,map类型数组h[a][sum]表示到a为止的前缀和为sum的a的对数,当两个a直接的字母的权值和为0时,即sum相同,这时h[a][sum]++,ans也相应+1,把所有字母遍历一遍就可以出答案了,时间复杂度时o(n),边敲边理解吧,我也理解了好久。
 1 #include<iostream>
 2 #include<map>
 3 #include<cstring>
 4 using namespace std;
 5 #define ll long long
 6 int main()
 7 {
 8   map<ll,int>h[26];//要注意sum是会爆int的
 9   int val[26];
10   string a;
11   for(int i=0;i<26;i++)cin>>val[i];
12   cin>>a;
13   ll sum=0,ans=0;
14   for(int i=0;i<a.size();i++)
15   {
16     ans+=h[a[i]-'a'][sum];
17     sum+=val[a[i]-'a'];
18     h[a[i]-'a'][sum]++;
19   }
20   cout<<ans<<endl;
21   return 0;
22 }

 


F - How Many Tables

 

Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.

InputThe input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.
OutputFor each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.
Sample Input

2
5 3
1 2
2 3
4 5

5 1
2 5

Sample Output

2
4

这道题就是典型的并查集的应用。初学并查集的时候我也是一脸懵逼,不过花多点时间还是搞懂了。直接讲不太好讲,边看代码边理解吧:
 1 #include<iostream>
 2 using namespace std;
 3 #include<vector>
 4  vector<int>fa;//fa[i]表示i的“父亲”
 5 int anc(int x)//找到这个点的“祖先”,“祖先”相同的点是在一桌的
 6 {
 7     return x==fa[x]?x:fa[x]=anc(fa[x]);
 8 //返回根节点,否则,递归查找根节点,并且fa[x]=root(路径压缩
 9 }
10 int add(int x,int y)//让这两点拥有相同的“祖先”
11 {
12     fa[anc(x)]=anc(y);
13 }
14 int main()
15 {
16     int T,N,M,r1,r2,root1,root2;;
17     cin>>T;
18     for(int j=0;j<T;j++)
19     {
20         cin>>N>>M;
21         for(int i=0;i<N;i++)fa.push_back(i);//最开始每个点的“祖先”都是自己,因此这时候需要N张桌子
22         for(int i=0;i<M;i++)
23         {
24             cin>>r1>>r2;//r1和r2有关系
25             root1=anc(r1-1);//找到r1的祖先
26             root2=anc(r2-1);//找到r2的祖先
27             if(root1!=root2){//如果它们的祖先不相同
28                 add(root1,root2);//就让它们的祖先相同
29                 N--;//桌子就少了一个
30             }
31         }
32         cout<<N<<endl;
33         fa.clear();
34     }
35 }

G- D-City

Luxer is a really bad guy. He destroys everything he met.
One day Luxer went to D-city. D-city has N D-points and M D-lines. Each D-line connects exactly two D-points. Luxer will destroy all the D-lines. The mayor of D-city wants to know how many connected blocks of D-city left after Luxer destroying the first K D-lines in the input.
Two points are in the same connected blocks if and only if they connect to each other directly or indirectly.

InputFirst line of the input contains two integers N and M.
Then following M lines each containing 2 space-separated integers u and v, which denotes an D-line.
Constraints:
0 < N <= 10000
0 < M <= 100000
0 <= u, v < N.
OutputOutput M lines, the ith line is the answer after deleting the first i edges in the input.Sample Input

5 10 
0 1 
1 2 
1 3 
1 4 
0 2 
2 3 
0 4 
0 3 
3 4 
2 4

Sample Output

1 
1 
1 
2 
2 
2 
2 
3 
4 
5

这题也是一个并查集的题目。题目是要求把给定的区域之间的连线删掉,然后求出没有连通的区域有多少块,这就相当于上一题的多少张桌子。可是现在只会“连线”,而不会“删线”,怎么办?那就反过来做呗,把输入数据倒过来,就相当于本来是没有线的,现在把他们连上,求出每次连接以后的连通块数量,再把它们反过来输出就得到结果了。

贴代码:
 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 const int MAXN = 100000+5;
 7 struct node
 8 {
 9     int x,y;
10 }res[MAXN];
11 int fa[10001];
12 int ans[MAXN];
13 int anc(int x)
14 {
15     return x==fa[x]?x:fa[x]=anc(fa[x]);
16 }
17 int add(int x,int y)
18 {
19     fa[anc(x)]=anc(y);
20 }
21 int main()
22 {
23     int N,M;
24     while(scanf("%d%d",&N,&M)==2)
25     {
26         for(int i=1;i<=M;i++)scanf("%d%d",&res[i].x,&res[i].y);
27         memset(ans,0,sizeof(ans));
28         memset(fa,0,sizeof(fa));
29         for(int i=0;i<N;i++)fa[i]=i;
30         ans[M]=N;
31     for(int i=M;i>1;i--)
32     {
33        int r1=anc(res[i].x);
34        int r2=anc(res[i].y);
35         if(r1!=r2)
36         {
37             add(r1,r2);
38             ans[i-1]=ans[i]-1;
39             //cout<<ans[i]<<endl;
40         }
41         else ans[i-1]=ans[i];
42     }
43     for(int i=1;i<=M;i++)printf("%d\n",ans[i]);
44     }
45 }

H - Largest Rectangle in a Histogram

A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:

Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.

InputThe input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1 <= n <= 100000. Then follow n integers h1, ..., hn, where 0 <= hi <= 1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.OutputFor each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.Sample Input

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

Sample Output

8
4000


这道题用暴力做法就会T,然后百度了下发现要用“单调栈”来做,搞了半天才懂。

顾名思义,单调栈就是这个栈里面存的数是单调的,单调栈做着道题很奇妙,建议边看代码边画图边理解,以我的水平不可能用打字来说清楚的:
 1 #include<iostream>
 2 #include<queue>
 3 #include<cmath>
 4 #include<algorithm>
 5 #define ll long long
 6 const int maxn = 1e5+2;
 7 using namespace std;
 8 int a[maxn],s[maxn],w[maxn];//s是要用的单调栈,w来记录宽度
 9 int main()
10 {
11   int n,p;
12   ll ans;
13   while(cin>>n&&n)
14   {
15     ans=0;p=0;
16     for(int i=1;i<=n;i++)cin>>a[i];
17     a[n+1]=0;//末尾要加一个0,看完以后可以回来想想为什么
18     for(int i=1;i<=n+1;i++)
19     {
20       if(a[i]>s[p])//如果当前高度比栈顶高度要大,直接入栈,这就保证栈是单调的
21       {
22         s[++p]=a[i];
23         w[p]=1;//宽度后面要用来算面积
24       }
25       else//如果当前高度比栈顶元素小
26       {
27         int width=0;
28         while(s[p]>a[i])//让栈顶出队直到栈顶比a[i]小
29         {
30           width+=w[p];////width用来记录出栈的矩形的总宽度
31           ans=max(ans,(ll)width*s[p]);//维护最大面积
32           p--;
33         }
34         s[++p]=a[i];
35         w[p]=width+1;//这个自己画图理解一下,为了算面积用的
36       }
37     }
38     cout<<ans<<endl;
39   }
40 }

 


做完day2,其实并没有很自信,毕竟大多数还是通过百度搞懂的,说不定过不久又丢的一干二净,还得经常回来复习才是,多多思考!

 

posted @ 2020-02-03 18:39  Hai_Lin  阅读(384)  评论(0)    收藏  举报