2019.08.31 日常总结

今天是2019年暑假的最后一天,明天就开学了,时间过得真快啊!

一本通1618&LOJ10196:

【题意】:监狱有连续编号为 1 到 n 的 n 个房间,每个房间关押一个犯人。有 m 种宗教,每个犯人可能信仰其中一种。如果相邻房间的犯人信仰的宗教相同,就可能发生越狱。求有多少种状态可能发生越狱。

【思路】:首先,我们要知道:会发生越狱的状态数=总状态数-不会发生越狱的状态数,所以我们分两部分来解决此问题:

一、总状态数:有n个房间,每个房间有m种可能的状态,所以总状态数为\prod _{i=1}^n m = m^n

二、不会发生越狱的状态数:第1个房间可以有m种状态,其余房间只能有(m-1)种状态(除了上一个房间的那种状态),所以不会发生越狱的状态数为m \times \prod^{n}_{i=2} (m-1) =m \times (m-1)^{n-1}

综上所述,会发生越狱的状态数为m^n-m\times (m-1)^{n-1},考虑到m和n都很大,直接计算会TLE,所以我们可以使用快速幂

【代码】:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=100003;
ll n,m;
ll ksm(ll a,ll b){//求a的b次方模mod的值
	register ll res=1ll;
	while (b){
		if (b&1) res=res*a%mod;
		a=a*a%mod;b>>=1;//记得每步取模
	}
	return res%mod;
}//快速幂的板子,考试是可以直接套进去
int main(){
	cin>>m>>n;//输入也别输入反了啊
	cout<<(ksm(m,n)-m*ksm(m-1,n-1)%mod+mod)%mod;
//	别忘了先加上mod再取模,因为ksm(m,n)可能小于m*ksm(m-1,n-1)%mod
	return 0;
}
//附两组数据吧:
//Input1:1000 2100           Output1:71246
//Input2:1000000(别数了,6个0) 1000000000000(12个0)           Output2:13558
//Input3:5 9                 Output3:25397

一本通1602&LOJ10180:

【题意】:

烽火台是重要的军事防御设施,一般建在交通要道或险要处。一旦有军情发生,则白天用浓烟,晚上有火光传递军情。

在某两个城市之间有 n 座烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确传递,在连续 m 个烽火台中至少要有一个发出信号。现在输入 n,m 和每个烽火台的代价,请计算总共最少的代价在两城市之间来准确传递情报。

【思路】:dp,记f[i]表示第i个烽火台可以发出信号,则f[i]=min \{f[j]+a[i] \}(i-m\leq j < i)

时间复杂度:O((n-m)m),当m=n\div 2时,时间复杂度达到O(n^2\div 4) = O(n^2),会TLE

改写原方程式,得:f[i] = min \{ f[j] \} + a[i](i-m \leq j < i),可以用单调队列优化,此时,时间复杂度降为O(n)

单调队列中应该保存有下标和f值,当然f不保存也可以,但保存了用起来方便

说一下单调队列的实现:可以手写一个队列,也可以用STL中的deque(双端队列,允许在两端快速删除和进入)

至于单调队列的原理、应用等和关于deque的知识,这里不在讲述,太长了,大家可以去网上搜索,或者有教科书的参考教科书

随便提一下,最后的答案并非f[n],因为最优解不一定要求第n个烽火台可以发出信号,但区间(n-m-1,n)中又必须要有一个烽火台可以发出信号,所以最终答案应该是min \{ f[i] \} (n-m+1 \leq i \leq n)

至此,本题就可以顺利AC(不过,在LOJ上好像不用单调队列也可以AC,但可以用就用吧,节省时间,还可以练练单调队列,以免考试时忘了)

【代码】:

//The AC code by hpwwzyy2012
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+3e2;
struct node{
	int subscr,value;
}q[N];int h,t,n,m,i,f[N],ans,a[N];
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
int main(){
//	freopen("t1.in","r",stdin);
	n=read();m=read();
	for(i=1;i<=n;i++)
	a[i]=read();
	q[0].subscr=0;//初始化一下,不用应该也OK吧
	for(i=1;i<=n;i++){
		while (h<=t&&q[h].subscr<i-m) h++;//排除不可选的解
		f[i]=f[q[h].subscr]+a[i];//此时,队头一定是可选的最优解
		while (h<=t&&q[t].value>a[i]) t--;//排除可选但非最优解
		q[++t]=(node){i,a[i]};//对于当前的单调队列而言,i一定是一个最优解
	}
	ans=0x3f3f3f3f;
	for(i=n-m+1;i<=n;i++)
	ans=min(ans,f[i]);
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值