CodeForces 1452 D - Radio Towers

CodeForces 1452 D - Radio Towers

题目大意:

n n n 个灯塔,每个灯塔有 1 / 2 1/2 1/2 的概率设一个信号发射器,覆盖长度可以自己设置 x x x, 要求 所有灯塔都要有信号,并且每个灯塔只能被一个信号覆盖,并且不能覆盖到 0 0 0 n + 1 n+1 n+1

思路:

设置 d p [ i ] dp[i] dp[i] 表示第 i i i 个灯塔刚好被覆盖的方案数,答案就是 d p [ n ] / 2 n dp[n] / 2^n dp[n]/2n,所以先求 d p [ n ] dp[n] dp[n] 即可

首先 d p [ i ] = d p [ i − 1 ] dp[i] = dp[i-1] dp[i]=dp[i1], 因为可以选择覆盖自己,即刚好从前面转移过来,然后就可以尝试覆盖别人即: d p [ i ] + = d p [ i − 2 ∗ k − 1 ] , k > = 1 dp[i] += dp[i-2*k-1] , k >= 1 dp[i]+=dp[i2k1],k>=1,这肯定是要判断边界的。这是什么意思呢,仔细观察这个,比如 d p [ 3 ] dp[3] dp[3] 就可以让 i = 2 i=2 i=2 覆盖 [ 1 − 3 ] [1-3] [13], 那么就可以从 d p [ 0 ] dp[0] dp[0] 转移过来,依次类推。

这里可以用奇偶前缀和数组优化,就可以过.

然后可以再扩展下这个公式:

首先 d p [ 0 ] = d p [ 1 ] = d p [ 2 ] = 1 dp[0] = dp[1] = dp[2] = 1 dp[0]=dp[1]=dp[2]=1

据一些例子 d p [ 3 ] = d p [ 2 ] + d p [ 0 ] dp[3] = dp[2] + dp[0] dp[3]=dp[2]+dp[0], 但是 d p [ 0 ] = d p [ 1 ] dp[0] = dp[1] dp[0]=dp[1], 所以可以写成 d p [ 3 ] = d p [ 2 ] + d p [ 1 ] dp[3] = dp[2] + dp[1] dp[3]=dp[2]+dp[1]

d p [ 5 ] = d p [ 4 ] + d p [ 2 ] + d p [ 0 ] = d p [ 4 ] + d p [ 2 ] + d p [ 1 ] = d p [ 4 ] + d p [ 3 ] dp[5] = dp[4] + dp[2] + dp[0] = dp[4] + dp[2] + dp[1] = dp[4] + dp[3] dp[5]=dp[4]+dp[2]+dp[0]=dp[4]+dp[2]+dp[1]=dp[4]+dp[3]

d p [ 7 ] = d p [ 6 ] + d p [ 4 ] + d p [ 2 ] + d p [ 0 ] = d p [ 6 ] + d p [ 5 ] dp[7] = dp[6] + dp[4] + dp[2] + dp[0] = dp[6] + dp[5] dp[7]=dp[6]+dp[4]+dp[2]+dp[0]=dp[6]+dp[5]

d p [ 4 ] = d p [ 3 ] + d p [ 1 ] = d p [ 3 ] + d p [ 2 ] dp[4] = dp[3] + dp[1] = dp[3] + dp[2] dp[4]=dp[3]+dp[1]=dp[3]+dp[2]

d p [ 6 ] = d p [ 5 ] + d p [ 3 ] + d p [ 1 ] = d p [ 5 ] + d p [ 3 ] + d p [ 2 ] = d p [ 5 ] + d p [ 4 ] dp[6] = dp[5] + dp[3] + dp[1] = dp[5] + dp[3] + dp[2] = dp[5] + dp[4] dp[6]=dp[5]+dp[3]+dp[1]=dp[5]+dp[3]+dp[2]=dp[5]+dp[4]

看到这里,应该可以找出规律,刚好就是经典的 F i b Fib Fib 数列

证明也不难,分奇偶数讨论

dp代码

#include <bits/stdc++.h>
using namespace std;
#define me(a, b) memset(a, b, sizeof(a))
#define IOS() ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'

typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 5;
const ll mod = 998244353;

ll mypow(ll a, ll b)
{
	ll res = 1;
	while(b) {
		if(b&1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

ll dp[maxn];
ll odd[maxn], even[maxn];
int main()
{
	IOS();
	int n;
	cin >> n;
	dp[0] = dp[1] = dp[2] = 1;
	odd[0] = 0;
	even[0] = odd[1] = odd[2] = 1;
	even[2] = 2;
	for(int i = 3; i <= n; ++i) {
		dp[i] = dp[i-1];
		if(i&1)
			dp[i] += even[i-3];
		else
			dp[i] += odd[i-3];
		dp[i] %= mod;
		odd[i] = odd[i-1];
		even[i] = even[i-1];
		if(i&1)
			odd[i] = (odd[i] + dp[i]) % mod;
		else
			even[i] = (even[i] + dp[i]) % mod;
	}
	// cout << dp[n] << endl;
	ll ans = dp[n] % mod * mypow(mypow(2, n), mod-2) % mod;
	cout << ans << endl;
	return 0;
}

Fib代码

#include <bits/stdc++.h>
using namespace std;
#define me(a, b) memset(a, b, sizeof(a))
#define IOS() ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'

typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 5;
const ll mod = 998244353;
ll mypow(ll a, ll b)
{
	ll res = 1;
	while(b) {
		if(b&1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

ll fib[maxn];
int main()
{
    IOS();
    int n;
    cin >> n;
    fib[1] = fib[2] = 1;
    for(int i = 3; i <= n; ++i)
        fib[i] = (fib[i-2] + fib[i-1]) % mod;
    ll ans = fib[n] * mypow(mypow(2, n), mod-2) % mod;
    cout << ans << endl;
    
    return 0;
}

总结:

很有意思的题目

CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值