20240820

赛时得分

题目 A B C D E F G H 总分 排名 比例
满分 1100 1300 1500 1700 1900 2100 2300 2800 14700 162 100%
得分 1100 1300 1500 1496 380 210 - - 5986 69 42.6%

A. 老 C 的输入法(1100/1100)

\(\text{100%}\) 得分做法,考虑直接创建每个字符串对应的九宫格序号,得到一个字符串,与答案串对比求个数即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
ll n,cnt;
string b[1001],tar;
string getstr(string s)
{
	string ans="";
	for(int i=0;i<s.length();i++)
	{
		if(s[i]>='a' and s[i]<='c') ans+="2";
		else if(s[i]>='d' and s[i]<='f') ans+="3";
		else if(s[i]>='g' and s[i]<='i') ans+="4";
		else if(s[i]>='j' and s[i]<='l') ans+="5";
		else if(s[i]>='m' and s[i]<='o') ans+="6";
		else if(s[i]>='p' and s[i]<='s') ans+="7";
		else if(s[i]>='t' and s[i]<='v') ans+="8";
		else if(s[i]>='w' and s[i]<='z') ans+="9";
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>b[i];
	cin>>tar;
	for(int i=1;i<=n;i++)
	{
		if(getstr(b[i])==tar) cnt++;
	}
	cout<<cnt;
	return 0;
}

B. coprime(1300/1300)

其实这题正解应该是分解质因子,然后记录每个质因子出现的次数,如果满足有质因子个数大于 \(2\),就不是 pairwise coprime,如果有质因子个数等于 \(n\),就不是 setwise coprime,剩下情况就是 not coprime

但是这题数据太水了,写了一发相邻两数求 gcd 都过了,我自己赛时也没反应过来这是个错解。所以就不挂代码了。


C. 问答(1500/1500)

挺不错的一道题目。\(\text{100%}\) 得分做法,我们考虑贪心,首先一定是每隔一个空连着放 \(k-1\) 个正确答案,看看能不能放得下。那么放不下的情况我们在一个一个空填进去。举个例子:

\(n=9,m=8,k=3\)
先每隔一个放:1 1 0 1 1 0 1 1 0
发现只放得下 \(6\) 个数,所以我们找到第一个和第二个 0 把他填进去,变成:1 1 1 1 1 1 1 1 0
不难发现这就是最优解。

接下来是算贡献的方案。

不难发现我们先按照每隔一个放下来的数目就是 \(\dfrac{n}{k}\times (k-1)+n%k\),如果这个数 \(\geq m\),那么我们直接输出 \(m\) 即可。

那么下面是放不下的方案,首先由于连着 \(k\) 个就会使当前贡献翻倍,那么我们不妨推一下:

\(1\times k\) 的贡献为 \(2\times k=k\times 2^1\)
\(2\times k\) 的贡献为 \(2\times(2\times k+k)=k\times (2^1+2^2)\)
\(3\times k\) 的贡献为 \(2\times(2\times(2\times k+k)+k)=k\times (2^1+2^2+2^3)\)

可得到 \(n\times k\) 的贡献为 \(k\times (2^1+2^2+\cdots +2^{n-1}+2^n)\),根据等比数列求和公式,\(2^1+2^2+\cdots +2^{n-1}+2^n=2^{n+1}-2\),所以前面部分联通的贡献就为 \(k\times (2^{i+1}-2)\),其中 \(i\) 为联通的区域个数。

那么最后再加上连通区域以外的答案个数 \(m-i\times k\) 即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int mod=1e9+9;
ll n,m,k,sum,i,ans;
ll qpow(ll a,ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return res;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>k;
	sum=n/k*(k-1)+n%k;
	if(sum>=m) cout<<m;
	else
	{
		i=m-sum;
		ans+=(qpow(2,i+1)-2)*k%mod;
		ans+=m-i*k;
		cout<<ans%mod;
		return 0;
	}
	return 0;
}

D. 发奖金(1700/1700)

\(\text{88%}\) 得分做法,首先我们以每个区间的最大值为 cmp 来排序,然后考虑以当前最大值的中位数的最小花费。

那么不难想到此时的方案:排在中位数之前的我们一律取左端点,排在中位数之后的我们去的是中位数和当前数左端点的 \(\max\) 值。

根据这个方案我们可以得到一个花费值,如果当前花费值已经 \(\leq s\) 了,我们输出当前花费值即可。

接下来我们需要考虑如果超过限制了怎么处理。对于每一个预期答案,我们都可以通过上述方案直接找到最小花费。那么我们考虑不断更新中位数,每次使中位数减小 \(1\),然后判断当前中位数的最小花费是多少,如果满足 \(\leq s\) 我们直接输出即可。

那么很容易会发现中位数其实不是固定的。因为当原来的中位数减到与其最小值相等的时候我们就没法继续操作下去了,因此我们需要更换操作对象,考虑在不断排序的同时遇到这种边界情况就将指针向右移一位,如果发现我们此时已经更新到了小于原中位数的位置,我们再将指针一会去即可。开一个变量 \(tar\) 并在结构体里记录原位置 \(pos\) 即可实现此操作。这样我们就会直观的判断出位置发生变化的情况。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,s,val,tar,lst;
struct lhm
{
	ll mi,ma,pos;
}a[N];
bool cmp(lhm a,lhm b)
{
	if(a.ma==b.ma) return a.mi<b.mi;
	else return a.ma<b.ma;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>s;
	for(int i=1;i<=n;i++) cin>>a[i].mi>>a[i].ma;
	if(n==1)
	{
		cout<<min(a[1].ma,s);
		return 0;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n/2;i++) val+=a[i].mi;
	val+=a[n/2+1].ma;
	for(int i=n/2+2;i<=n;i++) val+=max(a[n/2+1].ma,a[i].mi);
	for(int i=1;i<=n;i++) a[i].pos=i;
	tar=1;
	if(val<=s) cout<<a[n/2+1].ma;
	else
	{
		while(1)
		{
			sort(a+1,a+n+1,cmp);
			if(a[n/2+tar].pos!=lst) tar=1;
			while(1)
			{
				if(a[n/2+tar].mi==a[n/2+tar].ma) tar++;
				else break;
			}
			a[n/2+tar].ma--,val=0;
			lst=a[n/2+tar].pos;
			for(int i=1;i<=n/2+tar-1;i++) val+=a[i].mi;
			val+=a[n/2+tar].ma;
			for(int i=n/2+tar+1;i<=n;i++) val+=max(a[n/2+tar].ma,a[i].mi);
			if(val<=s)
			{
				cout<<a[n/2+tar].ma;
				return 0;
			}
		}
	}
	return 0;
}

\(\text{100%}\) 得分做法,考虑二分答案,统计区间在答案左侧和区间在答案右侧的区间数,然后不断往左边和右边放,如果答案可行就统计。一直二分即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,s,val,tar,lst;
struct lhm
{
	ll mi,ma;
}a[N],b[N];
bool cmp(lhm a,lhm b)
{
	if(a.mi==b.mi) return a.ma<b.ma;
	else return a.mi<b.mi;
}
bool check(ll mid)
{
	sort(a+1,a+n+1,cmp);
	ll cntl=0,cntr=0,val=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i].mi>mid)
		{
			val+=a[i].mi;
			cntr++;
		}
		else if(a[i].ma<mid)
		{
			val+=a[i].mi;
			cntl++;
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(a[i].mi<=mid and a[i].ma>=mid)
		{
			if(cntl<n/2)
			{
				cntl++;
				val+=a[i].mi;
			}
			else
			{
				cntr++;
				val+=mid;
			}
		}
	}
	if(val>s) return 0;
	return cntl<=n/2;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>s;
	for(int i=1;i<=n;i++) cin>>a[i].mi>>a[i].ma;
	sort(a+1,a+n+1,cmp);
	ll l=0,r=s,mid,ans=0;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(check(mid)==1)
		{
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	cout<<ans;
	return 0;
}
posted @ 2024-08-20 15:05  Lithium_Chestnut  阅读(7)  评论(0)    收藏  举报