11.15模拟赛 解题报告

也不知道这个写题解的人是出于什么考虑,写了等于白写,高考语文估计负分。这种模拟赛题解尸位素餐,因此这里写一个易懂的版本。
p.s. 小部分题目std还是较为易懂。

解题报告

原题呈现

https://www.luogu.com.cn/problem/U189344

题解

T1 选民

签到题。
为了下面叙述方便我们记11,10,01,01的选民分别有p1,p2,p3,p4个。
首先11的选民,选它不会有限制,所以全部选了。
其次考虑10和01,那么这里只讨论10的选民比01小于等于的情况(p2≤p3),如果反过来了就同理。那么就把这p2个10全部选了,再选p2个01,注意由于要使a权势值和最大,所以选01的时候要最优,也就是我们考虑选择所有01中ai最大的p2个。那么剩下的选民再不可能给拜登投票了,因此现在瓶颈已经确定了,相当于我们已经知道最多只能有2×(p1+p2)个选民,而由于现在我们已经确定了p1+2×p2个选民所以在剩下的01和00中我们最多只能选择p1个了,那就混到一起选ai最大的p1个,题目做完了。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,ans;
vector<int>a[5];
int main(){
//	freopen("voter.in","r",stdin);freopen("voter.out","w",stdout);
	cin>>n;
	string o;
	for(int i=1,x;i<=n;i++){
		cin>>o>>x;
		if(o=="00")a[4].push_back(x);
		else if(o=="01")a[3].push_back(x);
		else if(o=="10")a[2].push_back(x);
		else a[1].push_back(x);
	}
	for(int i=1;i<=4;i++)sort(a[i].begin(),a[i].end());
	if(a[2].size()>a[3].size())swap(a[2],a[3]);
	int cnt=a[1].size();
	for(int i=0;i<a[2].size();i++)ans+=a[3].back(),a[3].pop_back();
	while(cnt--){
		if(!a[3].size()&&!a[4].size())break;
		if(a[3].size()&&a[4].size()){
			if(a[3].back()>a[4].back())ans+=a[3].back(),a[3].pop_back();
			else ans+=a[4].back(),a[4].pop_back();
		}
		else if(a[3].size())ans+=a[3].back(),a[3].pop_back();
		else ans+=a[4].back(),a[4].pop_back();
	}
	for(int i=0;i<a[1].size();i++)ans+=a[1][i];
	for(int i=0;i<a[2].size();i++)ans+=a[2][i];
	cout<<ans;
}

T2 hihocoder

这个题是我赛时没有做出来的,赛后也没有自己想出来的,但是貌似AC率蛮高,说明本身不难。
是个DP。
考虑题目是什么意思。相当于这个n在二进制下如果写作一个01串的形式,那么a1....k必须形如下面那样子才行(指左边包含右边)。
image
然后题目的意思就是每一位横着加起来要满足 \(c_0*2^{0}+c_1*2^1+...c_{23}*2^23=n\)
那么想象n的二进制表示,对于每一个1的位置,比如说\(2^h\),那么它就等于\(2\times 2^{h-1}\),相当于每一个1的位置它向右double,double...然后最后合并起来刚好可以构造出c0,c1,c2这样子然后拆成a1,a2...ak,就成了一种方案。(应该说清楚了)
那么题目中有没有什么限制(或者说潜在的要求)?显然你的c0...c23都要≤k才行啊!
考虑设f[i]表示n=i时的答案。初状态f[0]=1;
摆出转移方程 f[i]=sum[i>>1]-sum[(i-k-1)>>1](其中sum[x]表示f[0]+f[1]+...+f[x])
为什么?我们关注c0。先解释sum[i>>1],那么sum[i>>1]每取到一个fj,c0就会每一个f[j]的方案得到一个值,因此就是f[i]产生f[j]种方案。那么如果c0>k就是说不对了,所以\(c1*2^1+c2*2^2+...+c_{23}*2^23\) 是不能小于(n-k)>>1的,所以要-sum[(i-k-1)>>1]。
综上所述这是一道思维有不小难度代码很短的DP题,需要非常清晰的思路和冷静的分析。

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e6+5,mod=998244353;
int n,k,f[N],sum[N];
signed main(){
	cin>>k>>n;
	f[0]=sum[0]=1;
	for(int i=1;i<=n;i++){
		f[i]=sum[i>>1];
		if(i>=k+1)f[i]-=sum[i-k-1>>1];
		f[i]=(f[i]+mod)%mod;
		sum[i]=(sum[i-1]+f[i])%mod;
	}
	cout<<f[n];
}

T3 IMO

这个题我是打表找规律做的,打表也不是一无是处吧,毕竟赛时遇到比较难的题目体力劳动把规律找出来不失为一个中策。
当然打表是一个比较笼统的说法,其实不一定非得打一个chart出来,重要的是把规律找到。
对于这道题,先去想?的情况肯定是不好想的,我们先把最基本的L(C)给看看怎么计算才得。
那么我第一步就是写了一个暴力的程序,这个题写暴力大概【入门-】难度吧。但是注意,为了找规律方便,强烈建议将程序中写成while(~scanf(...))的形式,可以流畅进行询问
然后我随便输了几个样例和乱想的字符串发现这样是徒劳的,要有方向地找规律。
于是我得到了一下下面的数据

5
HTHHT
000000111 7
6
HTHHTH
000001100 12
7
HTHHTHH
000010001 17
8
HTHHTHHH
000010110 22
9
HTHHTHHHH
000011011 27
10
HTHHTHHHHH
000100000 32

前面的是二进制不要管,事实证明这题跟二进制没啥关系。重要的是我们发现了这个等差数列!
但是问题接踵而至——公差是多少,首项是多少,又该如何确定?
接着我们在exe里输入H,HH,HHH...这样的串发现答案是1,2,3,...。然后又显然在一个串后面加T不会对L(C)有影响。但是我们发现HTH和HTTH的答案并不一样,于是我们猜想T肯定也会对L有性质上的影响。针对这一点进行实验。
HTH,HTTH,HTTTH,我们发现答案的公差每一次增加2.假如T前面不是H,换成更复杂的串,发现增加一个T也同样是让答案增加2的。因此我们猜想加T会对公差增加2,但对次数不变。
然后我们又有问题了,为什么HTHH和HHTH的答案不一样呢?一个简单的回答是,没有控制变量。末尾H的数量是不一样的。同时我们还遗留下来一个问题没有解决:首项。
再打表:HTH,HHTH,HHHTH,我们发现答案每次增加1,而H,HH,HHH不就是每次增加1吗,于是我们猜想首项就是f[i-1] (f[i]表示L(a[1...i]))。换句话说,如果a[i-1]=T,a[i],a[i+1],...是H,那么f[i-1],f[i]...,就应该构成公差为d[i-1]的等差数列,d[i-1]的计算方式为1~i-1中T的个数×2
第三步(我也不知道是不是第三步),验证,发现结论正确无误
第四步得出递推式:

当a[i]=='T'时,
f[i]=f[i-1]
d[i]=d[i-1]+2
当a[i]=='H'时,
f[i]=f[i-1]+d[i-1]
d[i]=d[i-1]

综上所述,打表是自然科学中的实验思维,是一切科学发现的滥觞

哦对,题目还要问带‘?’的吗,那有了上面的转移式这个就比较简单了

当a[i]=='T'时,
F[i]=F[i-1]
D[i]=D[i-1]+2*(2^(1...i-1内?的个数))
当a[i]=='H'时,
F[i]=F[i-1]+D[i-1]
D[i]=D[i-1]
当a[i]=='?'时
F[i]=F[i-1]+F[i-1]+D[i-1]
D[i]=D[i-1]+2*(2^(1...i-1内?的个数))+D[i-1]

稍做解释,F[i],D[i]分别表示\(\sum_{所有方案}\)的f[i]和d[i]。这个地方和最开始的转移式的不同在于,由于可能会有(2^(1...i-1内?的个数)) 种方案,所以原来的+2要变成+2*(2^(1...i-1内?的个数))

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5,mod=998244353;
int n,cnq,ans,f[N],d[N];
char s[N];
int qp(int a,int b){
	int c=1;
	for(;b;b>>=1,a=a*a%mod)if(b&1)c=c*a%mod;
	return c;
}
signed main(){
	//freopen("IMO.in","r",stdin);
	//freopen("IMO.out","w",stdout);
	scanf("%s",s+1),n=strlen(s+1);
	d[0]=1;
	for(int i=1;i<=n;i++){
		if(s[i]=='T'){
			f[i]=f[i-1];
			d[i]=d[i-1]+qp(2,cnq+1);
		}
		else if(s[i]=='H'){
			f[i]=f[i-1]+d[i-1];
			d[i]=d[i-1];
		}
		else {
			f[i]=f[i-1]*2+d[i-1];
			d[i]=d[i-1]*2+qp(2,cnq+1);
			cnq++;
		}
		f[i]%=mod,d[i]%=mod;
	}
	int _half=qp(2,mod-2);
	ans=f[n]*qp(_half,cnq)%mod;
	cout<<ans;
}

第四题我不会做,看起来也没有人打,算了不写了……

posted @ 2021-11-15 15:34  pengyule  阅读(26)  评论(0编辑  收藏  举报