Codeforces Round #838 (Div. 2) C(思维、dp)、D(思维、交互)

题目

C
题意: 给定长度为n的01序列,在两个数之间插入一个0或者1,形成一个序列叫做扩展。求f(1,1)+f(1,2)+f(1,3)+f(1,n). f是说有多少个扩展,满足扩展的所有奇数位置都是到那个位置为止的中位数。(也可以理解成出现次数更多)
思路: 反正这个题意有点抽象,但是读读样例能读懂。扩展的奇数位置就是原序列的每个位置,然后出现次数比另一个数多。当时想的是dp,表示每个位置有多少个灵活使用的空位。如果不需要灵活使用的空位,这些位置就是01任选,2的幂次的贡献,如果需要把所有灵活使用的空位都用上,那贡献只能是1. (晚上太困了,忘了由i-1的状态转移到i,不然还能上波分,问题不大,主要是理解的不够深)
PS: 如果第i位为1,第i个位置至少有i个1.因为扩展之后长度是2*i-1,如果要是中位数,至少需要i个1才可以。
题解更简单,如果第i个位置和第i-1个位置是一样的,那么贡献就是前i-1个位置的2倍,否则直接变为1.
其实很有道理,不妨用01来说明,现在两个位置都是1. 因为前i-1个位置是可以内部变换满足中位数那个限制的,也就是说前i-1个位置至少有i-1个1,不然他是满足不了限制的,而第i个位置又是1,前i个位置有i个1,已然满足条件,他前边的空位就01任选了。
而如果两个位置是01,前i-1个位置至少有i-1个0,1至多是i-2个,前i个位置有i-1个1,所以多出来的空位只能填1了。而且此时前i-1个空位也受到限制,只有一种方案了。因为前i-1个位置,恰好0比1多一个是一种方案,如果某个位置再多一个1变成0,最后第i个位置的1和空位的1都不足以让1的数量更多了。(这个我感觉难理解一些,但是当时手摸样例的时候就感觉是这样的,经典瞎猜)
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
const int mod = 998244353;
int n,m,k,T;
char s[N];
void solve()
{
	scanf("%d",&n);
	scanf("%s",s+1);
	ll ans = 0;
	ll now = 0;
	for(int i=1;i<=n;++i)
	{
		if(s[i]==s[i-1]) now = now * 2 % mod;
		else now = 1;
		ans = (ans + now) % mod;
	}
	printf("%lld\n",ans);
}
signed main(void)
{
	s[0] = 'a';
	scanf("%d",&T);
	while(T--)
	solve();
	return 0;
}

D
题意: 交互题,给定0到n-1的permutation。可以询问2n次,每次问两个不同下标的数的gcd。最后输出x、y,满足ax=0或者ay=0,就是找0在哪个下标。
思路: 基本是两次操作搞定一个数,但是没想明白,看了严格鸽题解才会。
首先n=2,特判,直接输出即可。
令x=1,y=2。始终维护两个有可能为0的下标。
此时比较gcd(x,3),gcd(y,3).
如果二者相等,3的位置不可能是0,不然不会相等,因为x和y不相等,gcd(0,x) = x.
如果二者不相等,假设gcd(x,3) < gcd(y,3)
那么可以排除x的位置是0的可能性。如果x的位置是0,gcd(x,3) = 3,不可能< gcd(y,3)的,gcd(y,3)顶多也就是3。所以令x = i,x的可能性排除但是i还不知道。
反之,可以排除y。
这样依次遍历3-n,即可排除掉n-2个非法下标,最后0一定在里边。太妙了,学不来。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e4+10;
int n,m,k,T;
int ask(int i,int j)
{
	cout<<"?"<<" "<<i<<" "<<j<<"\n";
	int x; cin>>x;
	return x;
}
void solve()
{
	cin>>n;
	int x = 1,y = 2;
	for(int i=3;i<=n;++i)
	{
		int t1 = ask(x,i);
		int t2 = ask(y,i);
		if(t1==t2) continue;
		if(t1<t2) //x的位置一定不是0,如果是0的话gcd更大 
		{
			x = i;
		}
		else y = i;
	}
	cout<<"!"<<" "<<x<<" "<<y<<"\n";
	cin>>n;
}
signed main(void)
{
	srand(time(NULL));
//	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--)
	{
//		cout<<"?\n";
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值