CodeForces Round #586(Div1+Div2)

A. Cards

测试地址
题意简述:
给定一个长度为 n 的字符串,该字符串可以组合出来几个one和几个zero,每个字符只能用一次。

代码示例:

#include
#include
using namespace std;
const int N = 1e5+10;
char str[N];
int nn;
void solve(){
	int n,z,e,r,o;
	n = z = e = r = o = 0;
	for(int i = 1;i <= nn;i++){
		if(str[i] == 'o') o++;
		else if(str[i] == 'z') z++;
		else if(str[i] == 'r') r++;
		else if(str[i] == 'e') e++;
		else n++;
	}
	int one,zero;
	one = min(n,min(o,e));
	n -= one, o -= one, e -= one;
	zero = min(o,min(e,min(z,r)));
	for(int i = 1;i <= one;i++) printf("1 ");
	for(int i = 1;i <= zero;i++) printf("0 ");
}
int main(){
	scanf("%d",&nn);
	scanf("%s",str+1);
	solve();
	return 0;
}

B. Multiplication Table

测试地址
题意简述:
给定一个n * n表格,其中 M i , j = a i ∗ a j M_{i,j} = a_i * a_j Mi,j=aiaj,现在有个坏蛋把序列a给扔了,同时把M_{i,i}都给扣走了,现在请你利用剩下的信息求出a序列。

解题思路:
不是高斯消元题,其实是个找规律题。如果我们能求出来 a 1 a_1 a1,那么我们就可以根据第一列求出所有答案。而我们又可以根据 a 1 a 2 = x a_1a_2 = x a1a2=x , a 1 a 3 = y a_1a_3 = y a1a3=y , a 2 a 3 = z a_2a_3 = z a2a3=z来求出 a 1 a_1 a1,于是就可以在求出其他所有结果了。
代码示例:

#include
#include
int n;
const int N = 1e3+10;
typedef __int64 ll;
ll mat[N][N],ans[N];
void solve(){
	ans[1] = mat[1][2]*mat[1][3]/mat[2][3];
	ans[1] = (ll)sqrt(ans[1]);
	for(int i = 2;i <= n;i++)
		ans[i] = mat[1][i]/ans[1];
	for(int i = 1;i <= n;i++) printf("%I64d ",ans[i]);
}
int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) 
		for(int j = 1;j <= n;j++) scanf("%I64d",&mat[i][j]);
	solve();
	return 0;
}

C. Substring Game in the Lesson

测试地址
题意简述:
Ann和Mike在玩游戏,有一个字符串s,初始时有l = r = k ,每个人轮流操作:

  • 选择l’ < l , r’ < r,且s[l’,r’] 字典序小于 s[l,r],然后令l = l’, r = r’。
  • 若不能做任何操作,则失败。

Ann先手,对于每个位置k,输出谁必胜。

题意简述:
简单的博弈论,首先必败态是“在 l 之前不存在l’ < l , 且s[l’ , r] 字典序小于 s[l ,r] ”,而若当前不是必败态,则一定可以使得下一个状态一定是必败态,于是只需要判断每个位置前面是否有字典序比其小的字符即可。

代码示例:

#include
#include
#include
using namespace std;
const int N = 5e5+10;
const int INF = 0x3f3f3f3f;
char str[N];
int vis[N];
void solve(){
	int mi = (int)str[0],n = strlen(str);
	for(int i = 1;i < n;i++){
		if(mi < (int)str[i]) vis[i] = 1;
		mi = min(mi,(int)str[i]);
	}
	for(int i = 0;i < n;i++)
		if(vis[i]) printf("Ann\n");
		else puts("Mike");
}
int main(){
	scanf("%s",str);
	solve();	
	return 0;
}

D. Alex and Julian

测试地址
题意简述:
给定一个正整数集合B,令全集Z(所有整数)内的元素作为无向图的顶点,对于图中内任意两点i 和 j,若abs(i - j)属于集合B,则 i 和 j 之间有一条无向边。现在请问最少删除B中几个顶点可以使得剩下的图是二分图。

解题思路:
不是二分图问题,只用到了“一张图是二分图,当且仅当图中不存在奇环”这一二分图判定定理。
至于为啥不能用二分图算法来解决呢,从问题规模就可以猜到,因为要构造图需要 O ( ( 1 e 18 ) 2 ) O((1e18)^2) O((1e18)2),显然n的规模不允许这样做。

那么从数学角度考虑(找规律),若有节点0和节点a,那么0 到 a有边,若还存在节点2 * a ,那么a 和2 * a也有边,且2 * a与 0也有边,这就是奇环了(哪怕还有3 * a,也还是奇环+一个偶环),因此不能有2 * a,同理也不能有4 * a,因此若想保留a,那么2 * a,4 * a, 8 * a, … 都要删去。
但是这样做法复杂度过高,因为顶点集合是[1, 1e18],挨个计算显然不现实,因此我们可以对集合B中每个元素b,B中有哪些元素可以和b一起被保留。

若a中有x个因数2,b中有y个因数2(x != y),那么a和b一定不能同时存在;假设c = lcm(a ,b)那么0 -> a -> 2 * a -> … -> c -> … -> 2 * b -> b -> 0一定可以构成一个奇环 ,因此a和b不能同时存在。所以若是x != y,则a和b不能同时存在(a和b都是集合B内元素)。

若是x = y,那么a和b可以同时存在,因为它们不存在a是b的倍数,或者b是a的倍数这种情况,也就不能删去0,因此若有环则一定是偶环。

综上所述,我们通过统计B集合中每个元素有多少个因子2即可判断最多同时存在多少个元素,即最少删去多少个元素。
代码示例:

#include
const int N = 2e5+10;
typedef __int64 ll;
ll a[N];
int tc[N],num[N],n;
void solve(){
	for(int i = 1;i <= n;i++){
		ll tmp = a[i];
		while(tmp && tmp%2 == 0) tc[i]++,tmp/=2;
		num[tc[i]]++;
	}
	int p = 0;	 
	for(int i = 1;i <= 64;i++)
		if(num[p] < num[i]) p = i;
	printf("%d\n",n-num[p]);
	for(int i = 1;i <= n;i++) 
		if(tc[i] != p) printf("%I64d ",a[i]);
}
int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%I64d",a+i);
	solve();	
	return 0;
} 

你可能感兴趣的:(CodeForces Round #586(Div1+Div2))