Educational Codeforces Round 157 (Rated for Div. 2)C,D

  C题

题目链接:C. Torn Lucky Ticket

C题要求寻找所有符合要求的拼串,那么一眼暴力就是枚举所有拼串组合的可能性,但这样必然会超时。

我们不妨从每一个数作为左边或者作为右边来入手,当某一个数作为左边的时候那么这个拼后的串的中间点就去这个数里面去寻找,可能是在某处也可能是这一整个数。那么这样的去寻找就可以将所有符合要求的组合找到。

那么我们选定一个数的某个部分作为拼后串的左半部分后,就可以确定右半部分需要总和是多少以及多长。在这里事先将每一个数的长度和总和求出来,那么在寻找的时候可以直接hash去寻找个数,加快速度。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5+10;
string str[maxn];
int str_len[maxn];
map<pair<int, int>,int> mp;

int calc(string s)
{
	int num = 0;
	for (int i=0;i<s.size();i++)
	{
		num += (s[i]-'0');
	}
	return num;
}

signed main()
{
	int n;
	int ans = 0;
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		cin>>str[i];
		int len = str[i].size();
		int val = calc(str[i]);
		str_len[i] = val;
//		cout<<len<<" "<<val<<endl;
		mp[{len, val}]++;
	}
	for (int i=1;i<=n;i++)
	{
		string s = str[i];
		int num = 0;
		for (int j=0;j<s.size();j++)
		{
			int len = s.size();
			num += (s[j]-'0');
			if ((j+1)-(len-j-1)<=0)
			{
				int sy_len = (len-j-1)-(j+1);
				int sy_num = (str_len[i]-num*2);
				ans += mp[{sy_len, sy_num}];
			}
			else
			{
				int sy = (j+1)-(len-j-1);
//				cout<<"j="<<j<<" nun="<<num<<endl;
//				cout<<sy<<" "<<num*2-(str_len[i])<<endl;
//				cout<<"val="<<mp[{sy, num*2-(str_len[i])}]<<endl;
				ans = ans + mp[{sy, num*2-(str_len[i])}];
			} 
		}
	}
	cout<<ans<<endl;
	return 0;
}

D题

这题考察的是异或位运算,看到位运算的题首先应该想到每一位的运算都是独立进行的。那么利用这个特点去解题。

然后异或运算的特点是相同为0不同为1,那么也就是说我们如果知道b1的值,就可以根据题目中所给的a数组将剩余的b数组的值推算出来。

那么现在问题就到了如何求b1的值上了。在题目中还有个条件就是b里面的数都是0~n-1的数,每个数只取一次。那么也就意味着b里面的数其实是0~n-1的排列。那么在某一个二进制位上1的个数就显而易见了。那么随后我们按位去假设b1的某一位是的二进制值是0,然后根据a去推演出剩下每个数的该位二进制值是多少。统计出有多少个1,如果符合条件那么就证明这一个二进制位就是0否则是1。

在将b1的每一位都推演出来之后,按照a里面的二进制数进行答案的推演即可。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int bit[maxn][31];
int ok_num[31];
int one_num[31];
int ans[maxn][31];
int cnt;
void calc(int pos, int x)
{
	int ct = 0;
	while (x)
	{
		bit[pos][ct] = x%2;
		x /= 2;
		ct++;
	}
	cnt = max(cnt, ct);
}

void calc2(int x)
{
	int num =0 ;
	while (x)
	{
		ok_num[num++] += x%2;
		x /= 2;
	}
}

int main()
{
	int n, k;
	cin>>n;
	for (int i=1;i<n;i++)
	{
		cin>>k;
		calc(i-1, k);
	}
	for (int i=1;i<n;i++) calc2(i);
	for (int i=0;i<cnt;i++)
	{
		ans[0][i] = 0;
		memset(one_num, 0, sizeof(one_num));
		for (int j=1;j<n;j++)
		{
			if (bit[j-1][i]==1)
				ans[j][i] = !ans[j-1][i];
			else ans[j][i] = ans[j-1][i];
			one_num[i] += ans[j][i];
		}
//		cout<<"one_num[i]="<<one_num[i]<<" "<<"ok_num[i]="<<ok_num[i]<<endl;
		if (one_num[i]!=ok_num[i]) ans[0][i] = 1;
	}
	for (int i=1;i<n;i++)
	{
		for (int j=0;j<cnt;j++) 
		{
			if (bit[i-1][j]==1)
			{
				ans[i][j] = !ans[i-1][j];
			} else {
				ans[i][j] = ans[i-1][j];
			}
		}
	}
	for (int i=0;i<n;i++)
	{
		int num = 0;
		for (int j=0;j<cnt;j++)
		{
//			cout<<ans[i][j]<<" ";
			num += pow(2,j)*ans[i][j];
		}
//		cout<<endl;
		cout<<num<<' ';
	}
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值