CF R1300-R1500(做题记录)

题目来源

  1. CF1574C Slay the Dragon
    添加链接描述
    排序后,用二分找大于等于x的第一个数,然后分情况讨论。

    1. 找到的数大于等于x:
      (1)那就要判断是用大于等于x的第一个数还是小于x的第一个数,因为有可能出现大的那个数很大然后有很多浪费的,而剩余数的和又比y小很多,所以要两个数的花费取最小值。
      (2) 如果找到的数是第一个数,没有必它小的了,那么只能用第一个数了。
    2. 找到的数小于x
      这也只有一种情况。直接算结果即可。

    注意点:
    1. 要开longlong
    2. 小技巧:ans = max(0ll, y - (sum - a[f]));//小技巧,小于0就不要

    #include<bits/stdc++.h> 
    #define int long long
    using namespace std;
    
    const int N = 2e5 + 10;
    int a[N];
    
    signed main()
    {
    	int n, sum = 0; cin >> n;
    	for (int i = 1; i <= n; i ++ ) { scanf("%lld", &a[i]); sum += a[i]; }
    	
    	sort(a + 1, a + 1 + n);
    	
    	int q; cin >> q;
    	
    	while (q -- )
    	{
    		int ans = 9999999999999999;
    		int x, y; scanf("%lld%lld", &x, &y);
    		int f = lower_bound(a + 1, a + 1 + n, x) - a;
    //		cout << f << endl;
    
    		if (a[f] >= x)
    		{
    			if (f != 1)//不是第一个比较选大的或者选小的结果 
    			{
    				ans = min(x - a[f - 1] + max(0ll, y -(sum - a[f- 1])), max(x - a[f], 0ll) + max(0ll, y -(sum - a[f])));
    				cout << ans << endl;
    			}
    			else //是第一个,只能选这一个
    			{
    				ans = max(0ll, y - (sum - a[f]));//小技巧,小于0就不要
    				cout << ans << endl;
    			 } 
    		}
    		else
    		{
    			f --;
    			ans = x - a[f] + max(0ll, y - (sum - a[f]));
    			cout << ans << endl;
    		}
    	}
    	return 0;
    }
    
  2. CF1573B Swaps
    原题链接

    1. 暴力思路:
      要想使a的字典序小于b就要使a的第一个数字小于b
      枚举a中的每一个数,并找到b中的第一个大于的数,算出移到最前面一共需要几步,取最小值。

    2. 优化思路:
      开一个数组存i这个奇数在b数组中的第一个大于i的偶数的下标
      然后再枚举一遍a,取最小值即可

    3. 注意点:
      要用快读快写,要开long long!

      #include<bits/stdc++.h>
      #define int long long
      using namespace std;
      
      inline int quick_read() noexcept {
      	int x = 0, f = 1;
      	char ch;
      	while (! isdigit (ch = getchar()))
      		if (ch == '-')
      			f = -f;
      	x = ch - '0';
      	while (isdigit (ch = getchar()))
      		x = (x << 3) + (x << 1) + ch - '0';
      	return x * f;
      }
      
      inline void quick_write(int x) {
      	if (x == 0)
      		putchar('0');
      	else {
      		int a[20] = {0};
      		int top = 0;
      		while (x) {
      			int temp = x % 10;
      			a[++ top] = temp;
      			x /= 10;
      		}
      		for (int i = top; i >= 1; i --)
      			putchar(a[i] + '0');
      	}
      }
      
      const int N = 1e6 + 10;
      int a[N], b[N], pos[N];
      
      signed main()
      {
      	int t = quick_read();
      	while (t -- )
      	{
      		int n; n = quick_read();
      		int ans = 99999999999;
      		for (int i = 1; i <= n; i ++ ) a[i] = quick_read();
      		for (int i = 1; i <= n; i ++ ) b[i] = quick_read();
      		for (int i = 1, j = 1; i <= n * 2; i += 2)
      		{
      			while  (b[j] < i) j ++;
      			
      			pos[i]  = j;
      		} 
      		for (int i = 1; i <= n; i ++ ) 
      		{
      			if (i - 1 + pos[a[i]] - 1 < ans) ans = i - 1 + pos[a[i]] - 1;
      		}
      		quick_write(ans);
      		puts("");
      	}
      	return 0;
      }
      
  3. CF1598C Delete Two Elements
    题目链接

    1. 思路:
      先求平均数,总和sum,平均数k,因为要使删除的两个数不影响剩下数的平均数,它们的和就是sum / n * 2,也就是说如果sum / n * 2%2 != 0,就说明此题无解,直接continue;所有数的出现次数用map记录,然后遍历所有数,设x = a[i], y = sum /n * 2 - x;
      当x == sum / n * 2时,y为0,这时它们的个数就是 x * (x - 1) (用到了组合计数公式,一串数字有n个,两两相组,一共有 n * (n - 1)种方法),因为遍历到这个数的时候,使用这个数的个数来计算,所以ans加完之后就给它的个数归0,
      当x != sum / n * 2时,就用 x * y,因为x个数和y个数一共有这么多种组合方法,算完之后给x,y的个数归0;

    2. 代码

      #include<bits/stdc++.h> 
      #define int long long
      const int N = 2e5 + 10;
      using namespace std;
      int a[N];
      signed main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int n; cin >> n;
      		int sum = 0;
      		map<int, int> num;
      		for (int i = 1; i <= n; i ++ )
      		{
      			scanf("%lld", &a[i]);
      			num[a[i]] ++;
      			sum += a[i];
      		}
      		
      		if (sum * 2 % n != 0) 
      		{
      			cout << "0" << endl;
      			continue;
      		}
      		
      		int k = sum * 2 / n;
      		
      		int ans = 0;
      		
      		for (int i = 1; i <= n; i ++ )
      		{
      			int x = a[i], y = k - x;
      			
      			if (num[x] == 0) continue;
      			
      			if (x == y)
      			{
      				ans += (num[x] * (num[x] - 1) / 2);
      				num[x] = 0; 
      			}
      			else 
      			{
      				ans += (num[x] * num[y]);
      				num[x] = 0;
      				num[y] = 0;
      			}
      			if (num[x] != 0) num[x] = 0;
      		}
      		cout << ans << endl;
      	}
      	return 0;
      }
      
  4. CF1594C Make Them Equal
    原题链接

    1. 思路
      最多只有两次操作,n和n - 1,可以使整个字符串都变成C,那么就只用考虑能否只用一次把这个字符串都变成C;
      通过

      for (int i = 2; i <= n; i ++ )
      {
      	f = 0;
      	for (int j = i; j <= n; j += i)
      	{
      		if (s[j - 1] != c)
      		{
      			f = 1;
      			break;
      		}
      	}
      	if (f == 0)
      	{
      		cout << 1 << endl << i << endl;
      		return;
      	}
      }
      

      思路:枚举每一位数和它的倍数,如果这一位它所有的倍数位都是合法的,那么选这一位,因为选了这一位之后可以使所有比这一位小的位数都变得合法(因为所有比这一位数小的都不能被这一位数整除),使比这一位大的数的中不能被这一位整除的所有数都变得合法,那也就是说把所有可能不合法的都变得合法了,那么只用选这一位就行了。

    2. 代码

      #include<bits/stdc++.h> 
      using namespace std;
      int n;
      char c;
      string s;
      
      void solve()
      {
      	bool f = 0;
      	cin >> n;
      	getchar();
      	cin >> c >> s;
      	
      	for (int i = 0; i < n; i ++ )
      	{
      		if (s[i] != c) {f = 1; break;}
      	}
      	
      	if (!f)
      	{
      		cout << 0 << endl; 
      		return;
      	}
      	for (int i = 2; i <= n; i ++ )
      	{
      		f = 0;
      		for (int j = i; j <= n; j += i)
      		{
      			if (s[j - 1] != c)
      			{
      				f = 1;
      				break;
      			}
      		}
      		if (f == 0)
      		{
      			cout << 1 << endl << i << endl;
      			return;
      		}
      	}
      	cout << 2 << endl << n << " " << n - 1 << endl;
      }
      
      int main()
      {
      	int t; cin >> t;
      	int cnt = 0;
      	int sum = 0;
      	
      	while (t -- )
      	{
      		solve();
      		
      	}
      	return 0;
      }
      
  5. CF1521B Nastia and a Good Array
    原题链接

    1. 思路
      想要使他变成每相邻的两个数都互质可以让这个数列变成:minn, minn + 1, minn, minn + 1…,因为没有规定要操作次数最小,所以可以这样进行操作;

    2. 注意点
      x, y要分别和a[i]和minn更换,所以换成minn时 x = minn, minn = minn,换成minn + 1时,x = minn + 1, y = minn。

    3. 核心代码

      if (abs(pos - i) % 2 != 0) printf("%d %d %d %d\n", i, pos, minn + 1, minn);
      else printf("%d %d %d %d\n", i, pos, minn, minn);
      
    4. 代码

      #include<bits/stdc++.h> 
      using namespace std;
      int main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int minn = 0x3f3f3f3f;
      		int pos = -1;
      		int n; cin >> n;
      		int num[n + 5];
      		for (int i = 1; i <= n; i ++ )
      		{
      			cin >> num[i];
      			if (num[i] < minn)
      			{
      				minn = num[i];
      				pos = i;
      			}
      		}
      		cout << n - 1 <<  endl;
      		for (int i = 1; i <= n; i ++ )
      		{
      			if (i == pos) continue;
      			if (abs(pos - i) % 2 != 0) printf("%d %d %d %d\n", i, pos, minn + 1, minn);
      			else printf("%d %d %d %d\n", i, pos, minn, minn);
      		}
      	}
      	return 0;
      }
      
  6. CF1562C Rings
    原题链接

    1. 思路(只需要满足一个能被另一个整除)
      ① 如果有大于 n/2 的全是0的字串,那么再随便找一个n / 2的字串即可。(100000,1000,0000)
      ② 如果遇到一个 0 ,如果这个位置i <= n / 2,那么就是这个位置做开头 i, n,i + 1,n;(101000,01000,1000)
      ③ 如果遇到一个 0,如果这个位置 i > n / 2,那么就是这个位置做结尾,1,i,1,i - 1 (如果是大于等于n/2,然后结尾是i + 1就有可能越界)(111110,11111,111110)
      ④ 如果一个0 都没有,就找两个相等的长度为n - 1的字串,这样也能满足被整除的要求,1,n-1,2,n;(1111111,111111,111111);
    2. 代码
      #include<bits/stdc++.h>
      using namespace std;
      const int N = 2e4 + 10;
      char s[N];
      int main() 
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int n;
      		scanf("%d%s", &n, s + 1);
      		bool f = 0;
      		for (int i = 1; i <= n; i ++ )
      		{
      			if (s[i] == '0')
      			{
      				f = 1;
      				if (i <= n / 2) printf("%d %d %d %d\n", i, n, i + 1, n);
      				else printf("1 %d 1 %d\n", i, i - 1);
      				break;
      			}
      		}
      		//需要长度相同,才能一样 
      		if (!f) printf("1 %d 2 %d\n", n - 1, n );
      	}
      	return 0;
      }
      
  7. CF1556B Take Your Places!
    原题链接

    1. 思路
      一 一枚举即可,注意一些细节。
      记录下原数组中所有奇数的位置和所有偶数的位置,然后和算出所有奇数和偶数距离它们应该所在的位置的距离。
      细节:
      ① 当n为偶数时,枚举所有奇数在奇数位,所有奇数在偶数位,所有偶数在奇数位,所有偶数在偶数位。
      ② 当n为奇数时,奇数位只能是多的那种数,那就特判一下哪个多,就计算一下那个数放到那一位的步数
      ③比较最小值即可。

    2. 代码

      #include<bits/stdc++.h> 
      #define int long long
      using namespace std;
      signed main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int n; scanf("%lld", &n);
      		int a[n + 10];
      		int aa = 0, b = 0;
      		int ji[n + 10], ou[n + 10];
      		for (int i = 1; i <= n; i ++ )
      		{
      			scanf("%lld", &a[i]);
      			if (a[i] % 2 == 0) 
      			{
      				ou[aa ++] = i;
      			}
      			if (a[i] % 2 != 0) 
      			{
      				ji[b ++] = i;
      			}
      		}
      		if (abs(aa - b) > 1) 
      		{
      			printf("-1\n");
      			continue;
      		}
      		
      		int ans = 0x3f3f3f3f3f3f3f3f;
      		int sum = 0;
      		
      		//n是偶数时有两种情况 
      		if (n % 2 == 0)
      		{
      			//把所有偶数放在偶数位上 
      			int cnt = 2;
      			sum = 0;
      			for (int i = 0; i < aa; i ++ )
      			{
      				sum = sum + abs(cnt - ou[i]);
      				cnt += 2;
      			}
      			ans = min (ans, sum);
      			 
      			//把所有偶数放在奇数位上 
      			cnt = 1, sum = 0;
      			for (int i = 0; i < aa; i ++ )
      			{
      				sum = sum + abs(cnt - ou[i]);
      				cnt += 2;
      			}
      			ans = min (ans, sum);
      			
      			//把奇数放在奇数位上
      			 cnt = 1, sum = 0;
      			for (int i = 0; i < b; i ++ )
      			{
      				sum = sum + abs(cnt - ji[i]);
      				cnt += 2;
      			}
      			ans = min (ans, sum);
      			
      			//把奇数放在偶数位上
      			 cnt = 2, sum = 0;
      			for (int i = 0; i < b; i ++ )
      			{
      				sum = sum + abs(cnt - ji[i]);
      				cnt += 2;
      			}
      			ans = min (ans, sum);
      		}
      		else //是奇数时只能把个数多的放在奇数位上 
      		{
      			sum = 0;
      			if (aa < b) //奇数多
      			{
      				//把偶数放到偶数位 
      				int cnt = 2, sum = 0;
      				for (int i = 0; i < aa; i ++ )
      				{
      					sum = sum + abs(cnt - ou[i]);
      					cnt += 2;
      				}
      				ans = min (ans, sum);
      				
      				//把奇数放到奇数位 
      				cnt = 1, sum = 0;
      				for (int i = 0; i < b; i ++ )
      				{
      					sum = sum + abs(cnt - ji[i]);
      					cnt += 2;
      				}
      				ans = min (ans, sum);
      			} 
      			else //偶数多 
      			{
      				//把偶数放到奇数位 
      				sum = 0;
      				int cnt = 1;
      				for (int i = 0; i < aa; i ++ )
      				{
      					sum = sum + abs(cnt - ou[i]);
      					cnt += 2;
      				}
      				ans = min (ans, sum);
      				
      				//把奇数放到偶数位
      				sum = 0; 
      				cnt = 2;
      				for (int i = 0; i < b; i ++ )
      				{
      					sum = sum + abs(cnt - ji[i]);
      					cnt += 2;
      				}
      				ans = min (ans, sum);
      			} 
      		}
      		printf("%lld\n", ans);
      	}
      	return 0;
      }
      
  8. CF1538C Number of Pairs
    原题链接

    1. 思路
      要找两个和在l和r范围内的数,那就先排序,然后枚举每个数a[i],然后找在a[i + 1]到a[n]之间有几个数在l - a[i] 和 r - a[i]之间,然后就是通过二分来查找范围内的数。

    2. 注意点(二分用法)
      lower_bound (a + 1, a + 1 + n,x) - a,找大于等于x的第一个数的下标。
      upper_bound (a + 1, a + 1 + n,x) - a,找大于x的第一个数的下标。
      如果数组中最大的数也小于x,那么返回的下标会是n + 1。
      所以找左范围的时候,其实要找的数是小于等于x的最后一个数,用upper找,找到的数是原本要找的那个数的下标+1,
      我们要求的是l - r之间有几个数,照原本的思路来看,用大于等于l的第一个数的下标 - 小于等于r的最后一个数,最终结果是 要+1的,但是实际上用upper找到的是r + 1,所以直接用r - l即可。
      主要考察二分函数的用法,代码中附带了二分函数测试器。

    3. 代码

      #include<bits/stdc++.h>
      #define int long long
      using namespace std;
      const int N = 2e5 + 10;
      int a[N];
      int b[N];
      
      signed main()
      {
      	int T; cin >> T;
      	while (T -- )
      	{
      		int ans = 0;
      		int n, l, r; scanf("%lld%lld%lld", &n, &l, &r);
      		for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
      		
      		sort(a + 1, a + 1 + n);
      		
      		for (int i = 1; i <= n; i ++ ) b[i] = a[n - i + 1];
      		
      //		for (int i = 1; i <= n; i ++ ) cout << a[i] << " ";
      		cout << endl; 
      		
      		for (int i = 1; i <= n; i ++ )
      		{
      			int ll = abs(l - a[i]), rr = abs(r - a[i]);
      			int aa = lower_bound(a + 1 + i, a + 1 + n, ll) - a;
      			int bb = upper_bound(a + 1 + i, a + 1 + n, rr) - a;
      //			printf("                             %d %d\n", aa, bb);
      			if (aa > bb) continue;
      			else
      			{
      				ans = ans + (bb - aa);
      			}
      		}
      		cout << ans << endl;
      		
      	}
      	return 0;
      }
      
      //二分函数的运用 
      //#include<iostream>
      //#include<cstdio>
      //#include<cstring>
      //#include<algorithm>
      //using namespace std;
      //int k,n=10;
      //int a[10]={1,1,1,3,3,5,5,5,5,6};
      //int main()
      //{
      //    for(int i=0;i<n;i++)cout<<a[i]<<" ";
      //    cout<<endl;
      //   while(scanf("%d",&k))
      //   {
      //       cout<<k<<"的第一个大于它的位置在"<<((upper_bound(a,a+n,k))-a)+1<<endl;
      //   }
      //}
      
  9. CF1516B AGAGA XOOORRR
    原题链接

    1. 异或和满足交换律,就像加法一样
    2. 任何数异或0不变
    3. 任何数和自己本身异或等于0
    4. 思路
      1. 既然满足交换律,那么顺序不变,从左往右依次异或,如果最终结果为0,那么说明最终数列中有偶数个相等的数的个数
      2. 如果不是0,说明肯定是奇数个,那么只要从左往右依次异或,看最后那个数出现了几次,只要大于2就都满足要求
      3. else,就是NO
    5. 代码
      #include<bits/stdc++.h>
      #define int long long
      using namespace std;
      const int N =  2005;
      int a[N];
      signed main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int n; scanf("%lld", &n);
      		int ans = 0;
      		for (int i = 1; i <= n; i ++ ) 
      		{
      			scanf("%lld", &a[i]);
      			ans ^= a[i];
      		}
      		if (ans == 0) //如果最终偶数个相同,那么就直接等于0了 
      		{
      			cout << "YES" << endl;
      			continue;
      		}
      		int ans1 = 0, cnt = 0;
      		for (int i = 1; i <= n; i ++ )
      		{
      			ans1 = ans1 ^ a[i];
      			if (ans1 == ans)
      			{
      				cnt ++;
      				ans1 = 0;
      			}
      		}
      		
      		//这便是判奇数,奇数不能使所有的数异或和为0,那么最后剩下的那个数,就是这个数列最终相等的数,那就看看这些相等的数的个数是否大于2 
      		if (ans1 == 0 && cnt >= 2) cout << "YES" << endl;
      		else cout << "NO" << endl;
      	}
      	return 0;
      }
      
      
  10. CF1520E Arranging The Sheep
    原题链接

    1. 思路,通过举例可以推出,最优方案是两边的*向最中间一个靠拢。
    2. 用a[i]到中间的距离减去中间有几个*。
    3. 注意特判所有位置都是*,没有*,和只有一个*的情况。
    4. 代码
      #include<bits/stdc++.h>
      #define int long long
      using namespace std;
      signed main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int n; scanf("%lld", &n);
      		string s; cin >> s;
      		int pos[n + 10], cnt = 0;
      		bool f = 0;
      		for (int i = 0; i < n; i ++ )
      		{
      			if (s[i] == '*')
      			{
      				pos[++cnt] = i + 1; 
      			}
      			if (s[i] == '.') f = 1;
      		}
      		if (!f || cnt == 0 || cnt == 1) 
      		{
      			cout << "0" << endl;
      			continue;
      		}
      		int ans = 0;
      		int mid = cnt / 2 + 1;
      		for (int i = 1; i <= cnt; i ++ )
      		{
      			if (i == mid)
      			{
      				continue;
      			}
      			ans = ans + abs(pos[mid] - pos[i]) - abs(mid - i);
      		}
      		printf("%lld\n", ans);
      	}
      	return 0;
      }
      
  11. CF1519C Berland Regional

    1. 题意
      有n个学生,每个学生属于某所大学,每个学生有一个能力值,n所大学。n次比赛,n次比赛分别是(1 - n人为一队),每所大学都只能自己组队,每k个人一队,最后剩下不足k个人就组不成一队,要求n次比赛所有参赛队伍的总能力值(也就是每所大学除去最后组不成一队的人,其他人的总能力值),还要使能力值尽可能大,也就是说能力值从大到小开始组队。
      2. 思路
      用二维vector存储每个学校的所有人的能力值,然后排序(从大到小),然后求一下前缀和,然后再用一层双重循环,先枚举每个学校,在枚举一下几个人组成一队,用 n/k*k - 1可以知道k人组队时这个学校有多少人参加,-1是因为数组从0 开始。
      3. 代码
      ```cpp
      #include<bits/stdc++.h>
      #define int long long int
      using namespace std;
      const int N = 2e5 + 10;

       vector<int> g[N];
       int s[N];
       int id[N];
       int ans[N];
       
       bool cmp(int xxx, int yyy)
       {
       	return xxx > yyy;
       }
       signed main()
       {
       	int t; cin >> t;
       	while (t -- )
       	{
       		
       		int n; scanf("%lld", &n);
       		for (int i = 1; i <= n; i ++ ) g[i].clear(), ans[i] = 0;
       		for (int i = 1; i <= n; i ++ ) scanf("%lld", &id[i]);
       		for (int i = 1; i <= n; i ++ ) scanf("%lld", &s[i]);
       		for (int i = 1; i <= n; i ++ ) g[id[i]].push_back(s[i]);
       		for (int i = 1; i <= n; i ++ )
       		{
       			//判断不为空 
       			if (g[i].size() != 0)
       			{ 
       				sort(g[i].begin(), g[i].end(), cmp);//排序
       				for (int j = 1; j < g[i].size(); j ++ ) //求前缀 
       				{
       					g[i][j] = g[i][j] + g[i][j - 1];
       				}
       			}
       		}
       
       		for (int i = 1; i <= n; i ++ )//求枚举每个大学 
       		{
       			if (g[i].size() != 0)//如果这个大学有人 
       			{
       				int len = g[i].size();
       				for (int k = 1; k <= len; k ++ ) //枚举分组 
       				{
       					//           加上当前大学的有效人数 
       					ans[k] += g[i][len / k * k - 1]; //该步骤可以实现去掉最后几个不足k人的人,-1是因为数组从0开始
       				}
       			}
       			
       		} 
       		for (int i = 1; i <= n; i ++ ) 
       		{
       			printf("%lld ", ans[i]);
       		}
       		cout << endl;
       	}
       	return 0;
       }
       ```
      
      1. 难点:
        主要二维vector不太会用。
  12. CF1558A Charmed by the Game
    原题链接

    1. 思路
      共a+b轮
      因为轮流发球,所以A和B分别只可能发球x次和y次
      int x = (a + b) / 2;
      int y = a + b - x;
      
      A赢a次, B赢b次。
      那么枚举A发球时赢的次数i,那么a的破发次数为a - i
      那么此时B的破发次数就是A发球输的次数,那么就是x - i
      注意判断,枚举A赢的次数时,它要小于A的发球次数,A的破发次数也要小于等于B的发球次数
      i <= x && a - i <= y
      
      还有当发球次数为y时的判断
      还要注意去重
    2. 代码
      #include<bits/stdc++.h>
      using namespace std;
      const int N = 2e5 + 10;
      int main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		int a, b; scanf("%d%d", &a, &b);
      		int x = (a + b) / 2, y = a + b - x;
      		map <int, int> f;
      		int cnt = 0, ans[N];
      		for (int i = 0; i <= a; i ++ )//枚举a发球时赢的次数
      		{
      			if (i <= x && a - i <= y) 
      			{
      				int flag = a - i + x - i;//a接球赢的次数+a发球输的次数
      				if (f[flag] == 0)
      				{
      					ans[cnt ++ ] = flag;
      					f[flag] ++;
      				} 
      			}
      		} 
      		
      		swap(x, y);
      		
      		for (int i = 0; i <= a; i ++ )//枚举a发球时赢的次数
      		{
      			if (i <= x && a - i <= y) 
      			{
      				int flag = a - i + x - i;//a接球赢的次数+a发球输的次数
      				if (f[flag] == 0)
      				{
      					ans[cnt ++ ] = flag;
      					f[flag] ++;
      				} 
      			}
      		} 
      		printf("%d\n", cnt);
      		sort (ans, ans + cnt);
      		for (int i = 0; i < cnt; i ++ ) printf("%d ", ans[i]);
      		printf("\n");
      	}
      	return 0;
      }
      
      注释详细版
      #include <bits/stdc++.h>
      using namespace std;
      int main()
      {
      	int t;
      	cin >> t;
      	while (t -- )
      	{
      		set<int> v;
      		int a, b;
      		cin >> a >> b;
      		//因为是轮流发球 
      		int x = (a + b) / 2; //A的发球次数 
      		int y = a + b - x;//B的发球次数 
      //		枚举A发球赢的次数 
      		for (int i = 0; i <= a; i ++ ) 
      		{
      //			A的赢的次数-A发球赢的次数==A接球赢的次数 (B发球输的次数) <= B的发球次数 
      //   		A发球赢的次数一定大于A发球的次数 
      			if (a - i <=  y && x >= i)
      			{
      //				破发总次数 
      				int flag = a - i + x - i;//a接球赢的次数+a发球输的次数(B接球赢的次数)
      				v.insert(flag);
      			}
      		}
      		
      		swap(a, b);
      		//因为是轮流发球 
      		x = (a + b) / 2; //A的发球次数 
      		y = a + b - x;//B的发球次数 
      //		枚举A发球赢的次数 
      		for (int i = 0; i <= a; i ++ ) 
      		{
      //			A的赢的次数-A发球赢的次数==A接球赢的次数 (B发球输的次数) <= B的发球次数 
      //   		A发球赢的次数一定大于A发球的次数 
      			if (a - i <=  y && x >= i)
      			{
      				int flag = a - i + x - i;//a接球赢的次数+a发球输的次数(B接球赢的次数)
      				v.insert(flag);
      			}
      		}
      		cout << v.size() << endl;
      		map<int, int> ma;
      		for (auto i:v)
      		{
      			cout << i << " ";
      		}
      		cout << endl;
      	}
      	return 0;
      }
      	
      
  13. CF1579D Productive Meeting
    原题链接

    1. 题意
      给你 n个数,每次操作从这 n 个数种挑出 2 个大于 0 的数,对这两个数分别 -1。
      求最大的可能的操作次数。
    2. 思路,使用优先队列,但是由于要输出的是下标,那么就用结构体优先队列,每次取出最大的两个数,-1,如果大于0就在扔回堆里,直到堆里的数的个数不足2为止。
    3. 代码
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 10;
    
    struct node
    {
    	int a, i;
    	
    	bool operator< (const node & x) const 
    	{
    		return a < x.a;
    	}
    };
    
    priority_queue<node> q;
    int main()
    {
    	
    	int t; cin >> t;
    	while (t -- )
    	{
    		while (!q.empty()) q.pop();
    		
    		int n; scanf("%d", &n);
    		
    		for (int i = 1; i <= n; i ++ )
    		{
    			int x;
    			scanf("%d", &x);
    			if(x > 0) q.push({x, i});
    			
    		}
    //		while (!q.empty())
    //		{
    //			node tt = q.top();
    //			cout << tt.i << " ";
    //			q.pop();
    //		}
    //		cout << endl << endl; 
    		int ans1[N];
    		int ans2[N];
    		int cnt = 0;
    		while (q.size()>= 2)
    		{
    			node x = q.top();
    			q.pop();
    			node y = q.top();
    			q.pop();
    			
    			x.a--;
    			y.a--;
    			ans1[cnt] = x.i;
    			ans2[cnt] = y.i;
    			cnt++;
    //			cout << x.i << y.i << endl;
    			if (x.a > 0) q.push(x);
    			if (y.a > 0) q.push(y);
    		}
    		cout << cnt << endl;
    		for (int i = 0; i < cnt; i ++ ) 
    		{
    			printf("%d %d\n", ans1[i], ans2[i]);
    //			cout << ans1[i] << " " << ans2 << endl;
    		}
    		
    	}
    	return 0;
    }
    
  14. CF1579C Ticks
    原题链接

    1. 题意,看图中所有星号部分,能否全部被划分为长度大于等于k的勾形。
    2. 思路
      存一个字符数组,再存一个数字数组记录状态(0为.,1为*),然后枚举每个*作为勾的交叉点,然后向上枚举,如果它的大小大于等于k,就将数字数字对应位置的1改为0,最后判断有没有全部都被改成0即可。
    3. 代码
       #include<bits/stdc++.h>
       using namespace std;
       const int N = 100 + 10;
       
       char g[N][N];
       int vis[N][N], n, m, k;
       
       void check(int x, int y)
       {
       	int cnt = 0;
       	while (1)
       	{
       		if (g[x - cnt][y - cnt] != '*' || g[x - cnt][y + cnt] != '*' || x < 1 || x > n || y < 1 || y > m) break;
       		cnt ++;
       	}
       	cnt -= 1;//因为初始是0时也判断了一次,所以-1 
       	if (cnt >= k)
       	{
       		vis[x][y] = 0;
       		for (int i = 1; i <= cnt; i ++ )
       		{
       			vis[x - i][y - i] = 0;
       			vis[x - i][y + i] = 0;
       		}
       	}
       }
       int main()
       {
       	int t; cin >> t;
       	while (t -- )
       	{
       		cin >> n >> m >> k;
       		memset(vis, 0, sizeof vis);
       		for (int i = 1; i <= n; i ++ )
       		{
       			for (int j = 1; j <= m; j ++ )
       			{
       				cin >> g[i][j];
       				if (g[i][j] == '*') vis[i][j] = 1;
       			}
       		}
       		for (int i = 1; i <= n; i ++ )
       		{
       			for (int j = 1; j <= m; j ++ )
       			{
       				if (g[i][j] == '*') check(i, j);
       			}
       		}
       		bool f = 0;
       		for (int i = 1; i <= n; i ++ )
       		{
       			for (int j = 1; j <= m; j ++ )
       			{
       				if (vis[i][j] == 1)
       				{
       					cout << "NO" << endl;
       					f = 1;
       					break;
       				}
       			}
       			if (f) break;
       		}
       		if (!f) cout << "YES" << endl;
       	}
       	return 0;
       }
    
  15. CF1545A AquaMoon and Strange Sort
    原题链接

    1. 思路
      因为要使它最后还都是向右的话,肯定要交换偶数次,那么只要当前数和它的目标位置的距离是偶数的话,那么它们交换之后还是都朝右的,其他数的交换不会影响到,因为换过来再换过去,别的数都还在原位。
      只要两个数所在位置的奇偶性相同,那么它们的距离就是偶数。
      那么只要在存数的时候记录一下这个数在奇/偶有几个,然后排序,看它和之前存的相比,有没有在相同位置上的数,有的话说明被用过了就-1,如果没有就说明它不符合要求,直接break输出NO即可。
    2. 代码
      #include<bits/stdc++.h> 
      using namespace std;
      const int N =  1e5 + 10;
      int num[N];
      int f[N][3];
      int main()
      {
      	int t; cin >> t;
      	while (t -- )
      	{
      		memset(f, 0, sizeof f);
      		int n; cin >> n;
      		for (int i = 1; i <= n; i ++ ) 
      		{
      			cin >> num[i];
      			f[num[i]][i % 2] ++ ;
      		}
      		sort(num + 1, num + 1 + n);
      		
      		bool flag = 0;
      		for (int i = 1; i <= n; i ++ )
      		{
      			if (f[num[i]][i % 2] == 0) //如果不存在和它在相同位置写上的数 
      			{
      				flag = 1; 
      				break;
      			}
      			//用完个数-1 
      			f[num[i]][i % 2] --;
      		}
      		if (flag) cout << "NO" << endl;
      		else cout << "YES" << endl;
      	}
      	return 0;
      }
      
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值