2019 第十六届浙江省赛部分题解

A:Vertices in the Pocket

DreamGrid has just found an undirected simple graph with n vertices and no edges (that's to say, it's a graph with n isolated vertices) in his right pocket, where the vertices are numbered from 1 to n. Now he would like to perform q operations of the following two types on the graph:

  • 1 a b -- Connect the a-th vertex and the b-th vertex by an edge. It's guaranteed that before this operation, there does not exist an edge which connects vertex a and b directly.
  • 2 k -- Find the answer for the query: What's the minimum and maximum possible number of connected components after adding k new edges to the graph. Note that after adding the k edges, the graph must still be a simple graph, and the query does NOT modify the graph.

 

Please help DreamGrid find the answer for each operation of the second type. Recall that a simple graph is a graph with no self loops or multiple edges.

Input

There are multiple test cases. The first line of the input is an integer T, indicating the number of test cases. For each test case:

The first line contains two integers n and q (1≤n≤105, 1≤q≤2×105), indicating the number of vertices and the number of operations.

For the following q lines, the i-th line first contains an integer pi (pi∈{1,2}), indicating the type of the i-th operation.

  • If pi=1, two integers ai and bi follow (1≤ai,bi≤n, ai≠bi), indicating an operation of the first type. It's guaranteed that before this operation, there does not exist an edge which connects vertex a and b directly.
  • If pi=2, one integer ki follows (0≤ki≤n(n−1)2), indicating an operation of the second type. It's guaranteed that after adding ki edges to the graph, the graph is still possible to be a simple graph.

 

It's guaranteed that the sum of n in all test cases will not exceed 106, and the sum of q in all test cases will not exceed 2×106.

Output

For each operation of the second type output one line containing two integers separated by a space, indicating the minimum and maximum possible number of connected components in this query.

Sample Input

1
5 5
1 1 2
2 1
1 1 3
2 1
2 3

Sample Output

3 3
2 3
1 2

题意:开始有n个独立节点,有两种操作,第一种操作:将点x和y连接;第二种操作:询问在将图内添加m条边后,联通块最少和最多的个数。

方法:最少的个数很好求,就是当前剩下的联通块减去添加的边的条数和1取较大值。而最多的个数就复杂了很多了。我知道的方法是线段树加二分。建立一棵树,叶子节点的编号表示联通块的大小,节点内存储大小为k的块的个数,可容纳的边的条数还有点的个数。最后查询的时候进行二分。但是这样我们二分到叶子节点k时就不知道要合并掉多少个大小为k的块了,所以我们在子叶子节点这里再做一次二分,这样复杂度仍然只有一个log2(n),因为每次查询我们只会到一个子叶节点。

#include 
#define mid (l+r)/2
using namespace std;
const int maxn=1e5+5;
int n,m,father[maxn],num[maxn];
struct node
{
	int point_num;//子树的点的个数之和
	long long edeg_num;//可容纳边的条数
	int num;//子树的块的个数
}no[maxn<<2];
void pushup(int cur)
{
	no[cur].edeg_num = no[cur<<1].edeg_num + no[cur<<1|1].edeg_num;
	no[cur].num = no[cur<<1].num+ no[cur<<1|1].num;
	no[cur].point_num = no[cur<<1].point_num + no[cur<<1|1].point_num;
}
int find(int x)
{
	return father[x]==x ? x : father[x]=find(father[x]);
}
void merge(int x,int y)
{
	father[find(y)]=find(x);
}
void bulid(int l,int r,int loc)
{
	if(r==1)
	//此时一定到了叶子节点了,并且此叶子节点的编号为1
	{
		no[loc].edeg_num=0;
		no[loc].num = no[loc].point_num=n;
		return;
		//表示大小为1的联通块的个数有n个,故子树的块数为n
	}
	else
	{
		no[loc].edeg_num = no[loc].num = no[loc].point_num=0;
		//其他大小的块个数为0
	}
	if(l != r)
	{
		bulid(l,mid,loc<<1);//宏定义了mid
		bulid(mid+1,r,loc<<1|1);
	}
}
void add(int l,int r,int loc,int x)
{
	if(l==r)
	{
		no[loc].point_num+=l;//添加了一个含有x个点的联通块
		no[loc].edeg_num+=l*(l-1)/2;//x点最多可容纳的边的条数
		no[loc].num++;//块的个数加一
		return;
	}
	if(x<=mid)
	{
		add(l,mid,loc<<1,x);
	}
	else
	{
		add(mid+1,r,loc<<1|1,x);
	}
	pushup(loc);
}
void erase(int l,int r,int loc,int x)
{
	if(l==r)
	{
		no[loc].point_num-=l;//删除了一个含有x个点的联通块
		no[loc].edeg_num-=l*(l-1)/2;//x点最多可容纳的边的条数
		no[loc].num--;//块的个数减一
		return;
	}
	if(x<=mid)
	{
		erase(l,mid,loc<<1,x);
	}
	else
	{
		erase(mid+1,r,loc<<1|1,x);
	}
	pushup(loc);
}
int query(int l,int r,int loc,long long k,long long v)
{
	if(v > n)exit(0);
	if(k<=0)return 1;
	if(l==r)
	{
		int ll=1,rr=no[loc].num;//二分合并块的个数
		while(ll < rr)
		{
			int mmid=(ll+rr)>>1;
			if((v+mmid*l)*(v+mmid*l-1)/2 >= l*(l-1)/2*mmid+k)rr=mmid;
			else ll=mmid+1;
		}
		return ll;
	}
	else
	{
		//优先合并右子树,因为右子树的块比左子树的块大,需要融合的块会少一些
		if((v+no[loc<<1|1].point_num)*(v+no[loc<<1|1].point_num-1)/2 >= k+no[loc<<1|1].edeg_num)
			return query(mid+1,r,loc<<1|1,k,v);
		else
			return query(l,mid,loc<<1,k+no[loc<<1|1].edeg_num,v+no[loc<<1|1].point_num)+no[loc<<1|1].num;
	}
}
int main()
{
	scanf("%*d");
	while(scanf("%d %d",&n,&m)==2)
	{
		for(int i=1;i<=n;i++)
		{
			father[i]=i;
			num[i]=1;
		}
		long long max_edge=0;//在不减少联通块的情况下能添加的最多边数
		int ans=n;//联通块的个数
		bulid(1,n,1);
		while(m--)
		{
			int op;
			scanf("%d",&op);
			if(op==1)
			{
				int x,y;
				scanf("%d %d",&x,&y);
				int fx=find(x),fy=find(y);
				if(fx != fy)
				{
					erase(1,n,1,num[fx]);//删除一个点的个数为num[fx]的块
					erase(1,n,1,num[fy]);
					max_edge -= num[fx]*(num[fx]-1)/2;
					max_edge -= num[fy]*(num[fy]-1)/2;
					num[fx] = num[fx]+num[fy];
					max_edge += num[fx]*(num[fx]-1)/2;
					add(1,n,1,num[fx]);
					merge(x,y);
					ans--;
				}
				max_edge--;
			}
			else
			{
				long long x;
				scanf("%lld",&x);
				int ww=query(1,n,1,x-max_edge,0);
				printf("%d %d\n",max(1ll,ans-x),ans+1-ww);
			}
		}
	}
}

B:Element Swapping

DreamGrid has an integer sequence a1,a2,…,an and he likes it very much. Unfortunately, his naughty roommate BaoBao swapped two elements ai and aj (1≤i

What's worse is that DreamGrid cannot remember his precious sequence. What he only remembers are the two values

x=∑k=1nkakandy=∑k=1nka2k

Given the sequence after swapping and the two values DreamGrid remembers, please help DreamGrid count the number of possible element pairs (ai,aj) BaoBao swaps.

Note that as DreamGrid is poor at memorizing numbers, the value of x or y might not match the sequence, and no possible element pair can be found in this situation.

Two element pairs (ai,aj) (1≤i

Input

There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases. For each test case:

The first line contains three integers n, x and y (2≤n≤105,1≤x,y≤1018), indicating the length of the sequence and the two values DreamGrid remembers.

The second line contains n integers b1,b2,…,bn (1≤bi≤105), indicating the sequence after swapping. It's guaranteed that ∑k=1nkbk≤1018 and ∑k=1nkb2k≤1018.

It's guaranteed that the sum of n of all test cases will not exceed 2×106.

Output

For each test case output one line containing one integer, indicating the number of possible element pairs BaoBao swaps.

Sample Input

2
6 61 237
1 1 4 5 1 4
3 20190429 92409102
1 2 3

Sample Output

2
0

题意:给定一个序列,这个序列有两个数字ai 和 aj 被交换位置了,现给出交换前计算出的X和Y。问被交换的ai 和 aj有几种可能。

方法:

用题目给的方法计算出当前序列的XY,用原来的减去现在的,结果如下:

(i-j)*(a[j]-a[i])=X-\sum_{i=1}^{n}k*a[i]     (1)

(i-j)*(a[j]^{2}-a[i]^{2})=Y-\sum_{i=1}^{n}k*a[i]^{2}    (2)

2式除1式,得到a[i] + a[j]。特判一下式子的右边等于0的情况

#include 
using namespace std;
const int maxn=1e5+5;
long long a[maxn];
long long num[maxn];
int main()
{
	scanf("%*d");
	int n;
	long long x,y;
	while(scanf("%d %lld %lld",&n,&x,&y)==3)
	{
		memset(num,0,sizeof(num));
		set se;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			x-=i*a[i];
			y-=i*a[i]*a[i];
			se.insert(a[i]);
			num[a[i]]++;
		}
		long long ans=0;
		if(x==0 && y==0)
		{
			for(auto it=se.begin(); it!=se.end(); it++)
			{
				if(num[*it] > 1)
				{
					ans+=(num[*it]*(num[*it]-1))/2;
				}
			}
		}
		else if(y!=0 && x!=0 && y%x==0)
		{
			long long t=y/x;//a[i]+a[j]
			for(int i=1;i<=n;i++)
			{
				long long w=t-a[i];//a[j]
				if(w>0 && w0)
				{
					int nape=a[i]-w;//a[i]-a[j]
					if(nape!=0&&x%nape==0)
					{
						int k=i+x/nape;//a[j]应该在的位置
						if(k>i && k<=n && a[k]==w)ans++;
					}
				}
			}
		}
		else ans=0;
		printf("%lld\n",ans);
	}
}

C:Array in the Pocket

BaoBao has just found an array A={a1,a2,…,an} of n integers in his left pocket. As BaoBao is bored, he decides to rearrange it into another array B={b1,b2,…,bn} of n integers such that B is a permutation of A, and ai≠bi for all 1≤i≤n. Please help BaoBao finish the rearragement.

If multiple valid rearrangements exist, find the smallest one among them.

Consider two arrays C={c1,c2,…,cn} and D={d1,d2,…,dn}, we say C is smaller than D if there exists an integer k such that 1≤k≤n, ci=di for all 1≤i

Input

There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases. For each test case:

The first line contains an integer n (1≤n≤105), indicating the length of the array.

The second line contains n integers a1,a2,…,an (1≤ai≤n), indicating the given array.

Output

For each test case output one line containing n integers b1,b2,…,bn separated by a space, indicating the answer. If there are multiple valid answers, output the smallest one. If no valid answer exists, print "Impossible" (without quotes) instead.

Please, DO NOT output extra spaces at the end of each line, or your answer may be considered incorrect!

Sample Input

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

Sample Output

1 2 4 3
2 3 1 1
Impossible

题意:给定一个a序列,输出字典序最小的b[i] 不等于a[i]的b序列。

方法:只有当某个数的个数有一半以上才会是无解的,其他情况都是有解的。但需要注意人为造成无解的情况,例如

序列:2 3 4 4 4 3

如果第一个数字填的是3,就会造成无解的情况。所以第一个数字必须填4。

我们可以用一个数组记录【待填的个数+不能填的位置】。则当数字x【待填的个数+不能填的位置】等于【当前剩余位置的个数】时,此位置必须填x。否则就选当前字典序最小的填入当前位置。

#include 
using namespace std;
const int maxn=1e5+5;
int a[maxn],b[maxn],num[maxn],num2[maxn];
typedef pair p;
int main()
{
	scanf("%*d");
	int n;
	while(scanf("%d",&n)==1)
	{
		memset(num,0,sizeof(num));//清空
		memset(num2,0,sizeof(num2));
		set

se,se2; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); num[a[i]]++;//num记录a[i]待填的个数 num2[a[i]]+=2;//num2记录a[i]待填的个数+b数组中不能填a[i]的位置的个数。开始的时候就是a[i]的个数的两倍 } for(int i=1;i<=n;i++) { if(num[i]) { se.insert(p(i,num[i]));//值与个数对应 se2.insert(p(num2[i],i));//个数与值对应 } } if((--se2.end())->first > n) { printf("Impossible\n"); continue; } for(int i=1;i<=n;i++) { se2.erase(p(num2[a[i]],a[i])); se2.insert(p(--num2[a[i]],a[i]));//a[i]不可填的位置个数少了一个,但待填的个数不变,故减一 p maxx=*(--se2.end());//se2的最后一个元素,即最大的【代填个数+不可填位置】 if(maxx.first == n-i+1)//若x待填的位置+不可填的位置个数的和 为 当前剩余的位置个数。\ 表明当前位置必须填x。若不填a[i],后面就会造成无解的情况 { b[i]=maxx.second; } else//若并非必须填x,则填入字典序最小的。这样可以保证最后得到的序列字典序最小 { if(a[i]==(se.begin())->first)//若集合中字典序最小的那个与a[i]相同,则填入字典序第二小的 { b[i]=(++se.begin())->first; } else { b[i]=(se.begin())->first; } } se.erase(p(b[i],num[b[i]]));se2.erase(p(num2[b[i]],b[i])); if(--num[b[i]])//若b[i]待填的个数不为零,则添加 { se.insert(p(b[i],num[b[i]])); } if(--num2[b[i]]) { se2.insert(p((num2[b[i]]),b[i])); } } for(int i=1;i<=n;i++) { if(i!=1)printf(" "); printf("%d",b[i]); } printf("\n"); } }

D:Traveler

E:Sequence in the Pocket

DreamGrid has just found an integer sequence in his right pocket. As DreamGrid is bored, he decides to play with the sequence. He can perform the following operation any number of times (including zero time): select an element and move it to the beginning of the sequence.

What's the minimum number of operations needed to make the sequence non-decreasing?

Input

There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:

The first line contains an integer (), indicating the length of the sequence.

The second line contains integers (), indicating the given sequence.

It's guaranteed that the sum of of all test cases will not exceed .

Output

For each test case output one line containing one integer, indicating the answer.

Sample Input

2
4
1 3 2 4
5
2 3 3 5 5

Sample Output

2
0

题意:给定n个数字,每个数字只能将其移动到第一个位置,求构成非递减序列最少需要移动几次。

方法:找到最大数,最大的数是可以不移动的。然后在最大的数前面找次大的数,找得到就继续找次次大的数。找到的这几个数都是可以不移动的。而其他的数都一定是需要移动的。

#include 
using namespace std;
const int maxn=1e5+5;
int a[maxn],b[maxn];
int main()
{
	scanf("%*d");
	int n;
	while(scanf("%d",&n)==1)
	{
		int maxa=0,maxi=0;
		for(int i=0;i=maxa)
			{
				maxa=a[i];
				maxi=i;
			}
		}
		sort(b,b+n);
		int xx=2;
		for(int i=maxi-1;i>=0;i--)
		{
			if(a[i]==b[n-xx])
			{
				xx++;
			}
		}
		printf("%d\n",n-xx+1);
	}
}

F:Abbreviation

In the Test of English as a Foreign Language (TOEFL), the listening part is very important but also very hard for most students since it is usually quite hard for them to remember the whole passage. To help themselves memorize the content, students can write down some necessary details. However, it is not easy to write down the complete word because of its length. That's why we decide to use the abbreviation to express the whole word.

It is very easy to get the abbreviation, all we have to do is to keep the consonant letters and erase the vowel. In the English alphabet, we regard 'a', 'e', 'i', 'y', 'o', 'u' as the vowels and the other letters as the consonants. For example, "subconscious" will be expressed as "sbcnscs".

However, there is one exception: if the vowel appears as the first letter, they should be kept instead of thrown away. For example, "oipotato" should be expressed as "optt".

Since you have learned how to use the abbreviation method, it's time for some exercises. We now present you words in total, it's your task to express them in their abbreviation form.

Input

There are multiple test cases. The first line of the input contains an integer (about 100), indicating the number of test cases. For each test case:

The only line contains a string () consisting of lowercase English letters, indicating the word which needs to be abbreviated.

Output

For each test case output one line containing one string, which is the correct abbreviation of the given word.

Sample Input

5
subconscious
oipotato
word
symbol
apple

Sample Output

sbcnscs
optt
wrd
smbl
appl

题意:给定一个字符串,将除第一个字符以外其他所有 'a', 'e', 'i', 'y', 'o', 'u' 删掉,输出删除后的串。

方法:水题,直接模拟就好。

#include 
using namespace std;
int main()
{
	string s;
	scanf("%*d");
	while(cin>>s)
	{
		string ans="";
		ans+=s[0];
		for(int i=1,j=s.length();i

G:Lucky 7 in the Pocket

BaoBao loves number 7 but hates number 4, so he refers to an integer as a "lucky integer" if is divisible by 7 but not divisible by 4. For example, 7, 14 and 21 are lucky integers, but 1, 4 and 28 are not.

Today BaoBao has just found an integer in his left pocket. As BaoBao dislikes large integers, he decides to find a lucky integer such that and is as small as possible. Please help BaoBao calculate the value of .

Input

There are multiple test cases. The first line of the input is an integer (about 100), indicating the number of test cases. For each test case:

The first and only line contains an integer (), indicating the integer in BaoBao's left pocket.

Output

For each test case output one line containing one integer, indicating the value of .

Sample Input

4
1
7
20
28

Sample Output

7
7
21
35

题意:输出最小的比n大的是7的倍数但不是4的倍数的数字。

#include
using namespace std;
int main()
{
	int a[]={0,7,14,21,35,42,49,63,70,77,91,98,105};
	scanf("%*d");
	int n;
	while(scanf("%d",&n)==1)
	{
		for(int i=1;;i++)
		{
			if(a[i]>=n)
			{
				printf("%d\n",a[i]);
				break;
			}
		}
	}
}

H:Singing Everywhere

Baobao loves singing very much and he really enjoys a game called Singing Everywhere, which allows players to sing and scores the players according to their performance.

Consider the song performed by Baobao as an integer sequence , where indicates the -th note in the song. We say a note is a "voice crack" if , and . The more voice cracks BaoBao sings, the lower score he gets.

To get a higher score, BaoBao decides to delete at most one note in the song. What's the minimum number of times BaoBao sings a voice crack after this operation?

Input

There are multiple test cases. The first line of the input contains an integer (about 100), indicating the number of test cases. For each test case:

The first line contains one integer (), indicating the length of the song.

The second line contains integers (), indicating the song performed by BaoBao.

It's guaranteed that at most 5 test cases have .

Output

For each test case output one line containing one integer, indicating the answer.

Sample Input

3
6
1 1 4 5 1 4
7
1 9 1 9 8 1 0
10
2 1 4 7 4 8 3 6 4 7

Sample Output

1
0
2

题意:最多删除一个数,使序列中的峰最少。

方法:暴力枚举出所有的峰,然后模拟。(代码很长,以后再缩)

#include 
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const ll inf=0x7fffffff;
ll a[maxn];
int main()
{
	scanf("%*d");
	int n;
	while(scanf("%d",&n)==1)
	{
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
		}
		
		a[0]=inf;
		a[n+1]=inf;
		
		int num=0;
		bool flag=0;
		for(int i=2;i<=n-1;i++)
		{
			if(a[i]>a[i-1] && a[i]>a[i+1])
			{
				num++;
				if(!flag)
				{
					if(a[i+2]>a[i+1] && a[i+2]>a[i+3] && i+3<=n)
					{
						if(a[i]==a[i+2])
						{
							num-=2;
//							cout<<"ok\n";
							if(num<0) {
								num=0;//cout<a[i+1] && a[i]>a[i-1])
				{
					if(a[i-2]==a[i] || a[i-1]==a[i+1] || a[i+2]==a[i])
					{
						i+=2;
						num--;
						break;
					}
					if(a[i-2]>=a[i] && a[i-2]>a[i-1] && a[i]>a[i-1])
					{
						i+=2;
						num--;
						break;
					}
					if(a[i-2]<=a[i] && a[i-2]=a[i] && a[i+2]>a[i-1] && a[i]>a[i-1])
					{
						i+=2;
						num--;
						break;
					}
					if(a[i+2]<=a[i] && a[i+2]

I:Fibonacci in the Pocket

DreamGrid has just found a Fibonacci sequence and two integers and in his right pocket, where indicates the -th element in the Fibonacci sequence.

Please tell DreamGrid if is even or is odd.

Recall that a Fibonacci sequence is an infinite sequence which satisfies , and for all .

Input

There are multiple test cases. The first line of the input contains an integer (about 100), indicating the number of test cases. For each test case:

The first and only line contains two integers and (). Their meanings are described above.

Output

For each test case output one line. If is even output "0" (without quotes); If is odd output "1" (without quotes).

Sample Input

6
1 2
1 3
1 4
1 5
123456 12345678987654321
123 20190427201904272019042720190427

Sample Output

0
0
1
0
0
1

题意:判断斐波那契数列a到b区间的和的奇偶性。

方法:我们可以发现数列的奇偶性为:奇奇偶  奇奇偶  奇奇偶  奇奇偶。任意连续的三个数的和都是偶数。可知若区间长度取余三为零,则和一定为偶数。若余1,我们可假设余下的那一个是该区间的第一个数(最后一个数也可以),那么只要判断这个数的奇偶性即可。若余二,可以假设余下的是该区间的第一个和第二个,那么只要判断前两个数的和的奇偶性即可。

例如给定的是a和b,可知:区间长度为len=(b-a+1),若len%3==0,直接输出0即可,否则输出1.

若len%3==1,我们需要判断a%3的值,若a%3==0,可知第a个数是偶数,否则是奇数。

若len%3==2,若a%3==1,可知第a个数和第a+1个数都是奇数,前两个数的和是偶数,否则前两个数的和一定是奇数。

方法二:可以看出前缀和奇偶性为:奇偶偶  奇偶偶  奇偶偶  奇偶偶。

这样我们只要把a和b取余三判断一下即可。只有 奇-奇 或 偶-偶是输出0,其他都输出1。

c++版本待补。

input()
while True:
    try:
        a,b=map(int,raw_input().split(" "))#输入,并分别赋值给a和b
        y=int(b)-int(a)+1;#y是区间长度
        if y%3 == 0:
            print(0)
        else:
            w = a%3
            if y%3 == 1:
                if w == 0:
                    print(0)
                else:
                    print(1)
            else:
                if w == 1:
                    print(0)
                else:
                    print(1)
    except:
        break

J:Welcome Party

The 44th World Finals of the International Collegiate Programming Contest (ICPC 2020) will be held in Moscow, Russia. To celebrate this annual event for the best competitive programmers around the world, it is decided to host a welcome party for all participants of the World Finals, numbered from to for convenience.

The party will be held in a large hall. For security reasons, all participants must present their badge to the staff and pass a security check in order to be admitted into the hall. Due to the lack of equipment to perform the security check, it is decided to open only one entrance to the hall, and therefore only one person can enter the hall at a time.

Some participants are friends with each other. There are pairs of mutual friendship relations. Needless to say, parties are more fun with friends. When a participant enters the hall, if he or she finds that none of his or her friends is in the hall, then that participant will be unhappy, even if his or her friends will be in the hall later. So, one big problem for the organizer is the order according to which participants enter the hall, as this will determine the number of unhappy participants. You are asked to find an order that minimizes the number of unhappy participants. Because participants with smaller numbers are more important (for example the ICPC director may get the number 1), if there are multiple such orders, you need to find the lexicographically smallest one, so that important participants enter the hall first.

Please note that if participant and are friends, and if participant and are friends, it's NOT necessary that participant and are friends.

Input

There are multiple test cases. The first line of the input contains a positive integer , indicating the number of cases. For each test case:

The first line contains two integers and (), the number of participants and the number of friendship relations.

The following lines each contains two integers and (), indicating that the -th and the -th participant are friends. Each friendship pair is only described once in the input.

It is guaranteed that neither the sum of nor the sum of of all cases will exceed .

Output

For each case, print a single integer on the first line, indicating the minimum number of unhappy participants. On the second line, print a permutation of to separated by a space, indicating the lexicographically smallest ordering of participants entering the hall that achieves this minimum number.

Consider two orderings and , we say is lexicographically smaller than , if there exists an integer (), such that holds for all , and .

Please, DO NOT output extra spaces at the end of each line, or your solution may be considered incorrect!

Sample Input

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

Sample Output

1
1 2 3 4
2
1 2 3 4

题意:n个人,m个关系,n个人进入会场,如果会场没有认识的人,则这个人会不高兴。注意,朋友关系之间不可传递,朋友的朋友不一定是朋友。要求不高兴的人数最少,并且输出字典序最小的入场方案。

方法:优先队列+并查集

#include 
using namespace std;
const int maxn=2e6+5;
int fa[maxn],ans[maxn];
bool vis[maxn];
vector ve[maxn];
int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
	int xx=find(x),yy=find(y);
	if(xx>yy)fa[xx]=yy;
	else fa[yy]=xx;
}
void bfs()
{
	priority_queue,greater >qq;
	qq.push(0);
	int tot=0;
	while(!qq.empty())
	{
		int x=qq.top();
		qq.pop();
		if(vis[x])continue;
		vis[x]=1;
		ans[tot++]=x;
		int num=ve[x].size();
		for(int i=0;iyy)fa[xx]=yy;
//			else fa[yy]=xx;
			merge(x,y);
		}
		int anss=0;
		for(int i=1;i<=n;i++)
		{
//			printf("%d ",fa[i]);
			if(fa[i]==i)
			{
				anss++;
				ve[0].push_back(i);
			}
		}
		bfs();
		printf("%d\n",anss);
		for(int i=1;i<=n;i++)
		{
			if(i!=1)printf(" ");
			printf("%d",ans[i]);
		}
		printf("\n");
	}
}

K:Strings in the Pocket

BaoBao has just found two strings and in his left pocket, where indicates the -th character in string , and indicates the -th character in string .

As BaoBao is bored, he decides to select a substring of and reverse it. Formally speaking, he can select two integers and such that and change the string to .

In how many ways can BaoBao change to using the above operation exactly once? Let be an operation which reverses the substring , and be an operation which reverses the substring . These two operations are considered different, if or .

Input

There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:

The first line contains a string (), while the second line contains another string (). Both strings are composed of lower-cased English letters.

It's guaranteed that the sum of of all test cases will not exceed .

Output

For each test case output one line containing one integer, indicating the answer.

Sample Input

2
abcbcdcbd
abcdcbcbd
abc
abc

Sample Output

3
3

题意:给两个字符串a和b,可以翻转a串的一个区间,使ab两串相等。问有多少个这样的区间。

方法:若两个串不相等,找到第一个不等和最后一个不等的位置,判断这个区间翻转过来能不能和第二个串相同。如果可以,就继续往外面扩散。

若两个串相等,用manacher找回文子串的个数。

特判只有一个字符不等的时候,减少时间复杂度。因为这题好像时间卡的特别死,多两个memset就超时了。

详情请看代码理解

#include
#define m(x) memset(x,0,sizeof(x));
using namespace std;
const int maxn=2e6+5;
char a[maxn],b[maxn];
int n,odd[maxn],eve[maxn];
long long manacher()
{
	int l=0,r=0,x;//x记录以当前串为中心能扩展的最大距离值
	long long ans=0;
	//当前for循环得到的回文串均为奇数长,即回文串的中心只有一个字符
	for(int i=1; i<=n; i++)//i为子串中心位置
	{
		if(i>r)x=1;//预处理此时能扩展的最远距离值。\
		当此时的中心串比前一次扩展的最远距离的下标大,表示无法进行预处理得到结果
		else x=min(odd[l+r-i],r-i);//(l+r-i)是在原回文串的对称位置的下标
		while(i-x>=1&&i+x<=n&&a[i-x]==a[i+x])x++;//以此为中心可以往两边继续扩散
		odd[i]=x;//以i为中心可以扩展的最大距离
		ans+=x;//每增加一个距离,回文串多一个
		if(i+x-1>r)//当前串能往两边扩展的最大距离比前一个串能扩大的最远距离大,就更新最大长度
		{
			r=i+x-1;//当前串往后扩展的最远位置的下标
			l=i-x+1;//往前扩展的下标
		}
	}
	l=r=0;
	//这个循环处理回文串中心串为两个字符的情况
	for(int i=1; i<=n; i++)
	{
		if(i>r)x=0;//x记录以i和(i-1)为中心串,对i而言所能扩展的最大距离
		else x=min(eve[l+r-i+1],r-i+1);//预处理x的值。\
		这个地方真的很有意思(l+r-i+1)对应的字符和i所对应的字符不一定是同一个字符,但扩展得到的结果是一样的。\
		如果用(l+r-i)得到的是用一个字符,但扩展得到的结果不同。最后才发现其实根本没必要是同一个字符
		while(i-x-1>=1&&i+x<=n&&a[i-x-1]==a[i+x])x++;//若x为0,先判断i能否和(i-1)做中心串。然后往两边扩散
		eve[i]=x;//记录以i和(i-1)为中心串,对i而言所能扩展的最大距离
		ans+=x;//回文串的个数
		if(i+x>=r)//更新长度
		{
			l=i-x;//往前扩展的下标
			r=i+x-1;//往后扩展的下标
		}
	}
	return ans;
}
int main()
{
	scanf("%*d");
	while(scanf("%s",a+1)==1)//下标从a[1]开始
	{
		scanf("%s",b+1);
//		m(odd);m(eve);//不要加,会超时,而且也没必要加
		n=strlen(a+1);//计算字符串的长度
		int l,r;
		for(l=1;l<=n;l++)if(a[l]!=b[l])break;
		for(r=n;r>=1;r--)if(a[r]!=b[r])break;
		if(l==r)//表示只有一个字符不等,这种时候无论怎么翻转都不可能相等
		{
			printf("0\n");
			continue;
		}
		else if(l<=n)
		{
			int ans=1;//记录回文串的个数,
			for(int i=l;i<=r;i++)//判断两个串翻转后是否相等
			{
				if(a[i]!=b[l+r-i])//此时无法做到翻转后相等
				{
					ans=0;
					break;
				}
			}
			if(ans)//翻转后可以相等
			{
				l--;
				r++;
				while(l>=1&&r<=n&&a[l]==b[r]&&a[r]==b[l])//将两个串往两边扩散,即找更大的回文串
				{
					l--;
					r++;
					ans++;
				}
			}
			printf("%d\n",ans);
		}
		else//两个串完全相等
		{
			printf("%lld\n",manacher());
		}
	}
}

 

你可能感兴趣的:(ACM)