HGOI-国庆七连测-day6

题解

今天是沙雕题集合…第一题就要打150+但毫无营养,第三题一题更比四题强…蒟蒻实在是没有办法orz
有生之年不要让我碰到那个出题人
德州扑克真的沙雕


在这里插入图片描述


第一题——德克萨斯扑克(texas)

在这里插入图片描述

在这里插入图片描述


  • 题目太长了orz,懒得概括…
  • 就是一个暴力模拟+注意细节+要耐心
  • 这个是我这辈子打的最刺激的第一题。

#include <bits/stdc++.h>
using namespace std;
inline void fff(){
	freopen("texas.in","r",stdin);
	freopen("texas.out","w",stdout);
}
int a[20],ans[6],pp;
char ch[10];
void check4(){
	for(int i=14;i>1;i--){
		if(a[i]==4){
			for(int j=1;j<=4;j++) ans[++pp]=i;
			a[i]=0;
		}
	}
	if(pp==0) return;
	for(int i=14;i>1;i--){
		if(a[i]) {
			ans[++pp]=i;
			return;
		}
	}
}
void check3_1(){
	if(pp==5) return;
	int pos1=-1;
	for(int i=14;i>1;i--){
		if(a[i]==3){
			for(int j=1;j<=3;j++) ans[++pp]=i;
			pos1=i;
			break;
		}
	}
	if(pp==0) return;
	bool flag=false;
	for(int i=14;i>1;i--){
		if(a[i]>=2&&i!=pos1){
			for(int j=1;j<=2;j++)ans[++pp]=i;
			a[i]=0;
			flag=true;
			return;
		}
	}
	if(!flag){
		memset(ans,0,sizeof(ans));
		pp=0;
	}
}
bool sss=false;
void checks(){
	if(pp==5) return;
	for(int i=14;i-5>=1;i--){
		bool flag=true;
		for(int j=i;j>i-5;j--)if(a[j]==0) flag=false;
		if(flag){
			for(int j=i;j>i-5;j--){
				ans[++pp]=j;
			}
			return;
		}
	}
	if(pp==5) return;
	if(a[14]){
		for(int i=5;i>=2;i--){
			if(a[i]==0) return;
		}
		sss=true;
		for(int j=5;j>1;j--){
				ans[++pp]=j;
		}
		ans[++pp]=14;
	}
}
void check3_2(){
	if(pp==5) return;
	for(int i=14;i>1;i--){
		if(a[i]==3){
			for(int j=1;j<=3;j++) ans[++pp]=i;
			a[i]=0;
		}
	}
	if(pp==0) return;
	for(int i=14;i>1;i--){
		if(a[i]){
			ans[++pp]=i;
			a[i]=0;
		}
		if(pp==5) return;
	}
}
void check2_1(){
	if(pp==5) return;
	for(int i=14;i>1;i--){
		if(a[i]==2){
			for(int j=1;j<=2;j++) ans[++pp]=i;
			a[i]=0;
		}
		if(pp==4) break;
	}
	if(pp==0) return;
	for(int i=14;i>1;i--){
		if(a[i]) {
			ans[++pp]=i;
		}
		if(pp==5) return;
	}
}
void check1(){
	if(pp==5) return;
	for(int i=14;i>1;i--){
		if(a[i])ans[++pp]=i;
		if(pp==5) return;
	}
}
void print(int i){
	switch(i){
		case 14:printf("A ");break;
		case 11:printf("J ");break;
		case 12:printf("Q ");break;
		case 13:printf("K ");break;
		default: printf("%d ",i);
	}
}
int main(){
	fff();
	pp=0;
	for(int i=1;i<=7;i++){
		cin>>ch;
		switch(ch[0]){
			case '1':a[10]++;break;
			case 'A':a[14]++;break;
			case 'J':a[11]++;break;
			case 'Q':a[12]++;break;
			case 'K':a[13]++;break;
			default: a[ch[0]-'0']++;
		}
	}
	check4();
	check3_1();
	checks();
	check3_2();
	check2_1();
	check1();
	sort(ans+1,ans+6);
	if(!sss){
		for(int i=5;i>=1;i--){
			print(ans[i]);
		}
	}else{
		for(int i=4;i>=1;i--)print(ans[i]);
		print(14);
	}
}

第二题——战斗(battle)

【题目描述】

  • 给出一个长度为 n n 的非下降序列,要求从中选出长度至少为 k k 的子序列,并且从中选出一个值满足序列的方差最小
  • 求最小方差

  • 方差公式:令 x 0 x_0 为基准。 S = ( x 1 x 0 ) 2 + ( x 2 x 0 ) 2 + ( x 3 x 0 ) 2 + . . . + ( x k 1 x 0 ) 2 S=(x_1-x_0)^2+(x_2-x_0)^2+(x_3-x_0)^2+...+(x_{k-1}-x_0)^2
  • 首先可以推出几个结论:
    • 第一,个数至多为k,如果增加到k+1个,若子序列当中所有元素相同,则无影响,多一个也没意义,如果不同,那么加一个只能令方差增大。
    • 第二,这 k k 个元素一定是挨在一起的。这个假设一下就好了。
  • 然后把这个方差公式展开一下:
    • S = x 1 2 + x 2 2 + x 3 2 + . . . + x k 1 2 + ( k 1 ) x 0 2 2 x 0 ( x 1 + x 2 + x 3 + . . . x k 1 ) S=x_1^2+x_2^2+x_3^2+...+x_{k-1}^2+(k-1)*x_0^2-2*x_0*(x_1+x_2+x_3+...x_{k-1})
    • S = i = 1 k 1 x i 2 + ( k 1 ) x 0 2 2 x 0 i = 1 k 1 x i S=\sum_{i=1}^{k-1}{x_i^2}+(k-1)*x_0^2-2*x_0 *\sum_{i=1}^{k-1}{x_i}
  • 然后前缀和跟前缀平方和就可以 O ( 1 ) O(1) 求出某段区间内的选择某一个做基准的方差值
  • 考虑这个方程发现这个方程式式一个二次函数,但他好像不是完全严格递减递增的,有可能长下面这个样子:

在这里插入图片描述

  • 所以二分法的时候就有可能停留在中间的某一段上面orz
  • 这个时候就要再用一次二分法找到之前一个和这个值不一样的点来找到单调的区间。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
inline void fff(){
	freopen("battle.in","r",stdin);
	freopen("battle.out","w",stdout);
}
inline LL read(){
	LL x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return x;
}
const int N=1e5+10;
LL n,k;
LL a[N];
LL sum[N],sum_s[N];
LL get_s(LL x,LL l,LL r){
	return sum_s[r]-sum_s[l-1]+a[x]*a[x]*(r-l+1)-2*(sum[r]-sum[l-1])*a[x];
}
LL find_(LL st,LL m){
    LL l=st,r=m-1,an=l-1;
    while(l<=r){
        LL mid=(l+r)>>1;
        if(a[mid]<a[m]) l=(an=mid)+1;
        else r=mid-1;
    }
    return an;
}

int main(){
//	fff();
	n=read();
	k=read();
	for(LL i=1;i<=n;i++){
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
		sum_s[i]=sum_s[i-1]+a[i]*a[i];
	}
	LL ans=sum_s[n];
	if(n<=1000){
		for(LL i=1;i<=n;i++){
			if(i+k-1>n) break;
			for(int j=i;j<=i+k-1;j++){
				ans=min(ans,get_s(j,i,i+k-1));
			}
		}
	}else{
		for(LL i=1;i<=n;i++){
			if(i+k-1>n) break;
			LL l=i,r=i+k-1;
			LL L=l,R=r;
			while(l<r){
				LL mid=(l+r)>>1;
				LL p1=find_(i,mid);
				if(get_s(mid,L,R)-get_s(p1,L,R)<0||p1==i-1){
					ans=min(get_s(mid,L,R),ans);
					l=mid+1;
				}else{
					ans=min(ans,get_s(p1,L,R));
					r=mid-1;
				}
			}
		}
	}
	cout<<ans;
}

第三题——计数(count)

在这里插入图片描述


  • 这道题一题更比四题强(而且只给2s256MB)
  • 还好题目本身比较良心给了部分分,不然这个出题人第二天就会暴毙家中的。
  • 第一问还行,翻译一下就是因数个数,然后特判一下0的情况,水水的就过去了。
  • 注意到这一问中的路径可以通过第三问中的路径生成(通过在一些点上走自环)。设有一条从1 到0、长度为k、没有经过重复点的路径。我们需要计算出这条路径能生成出的问题二中的路径数量,即求出:在这条路径上的若干个点上走若干次自环,且最后的路径长度不大于q2 的方案数。
  • 设路径上第i 个节点在最后的路径上出现了xi 次。则上述问题等价于求 x 1 + x 2 + . . . + x k q 2 x_1 + x_2 +... + x_k\leq q_2 的正整数解的个数。用一些组合数学知识可以知道答案是
    C q 2 k C^k_{q_2}
  • 考虑dp 到节点 i , 2 i , 3 i , . . . n i i i,2i,3i,... \lfloor \frac{n}{i} \rfloor i 那么之后能够到达的非零节点只有如果把这些点重标号为 1 , 2 , 3... n i 1,2,3... \lfloor \frac{n}{i} \rfloor ,在这张图上考虑从1 到0 的路径,得到的答案和原来相同。
  • 所以总状态只有 O ( l o g &ThinSpace; n n ) O(log\,n\sqrt{n})
  • 然后再算上之前的复杂度,总的复杂度大概就是 O ( n 3 4 l o g &ThinSpace; n ) O(n^{\frac{3}{4}}*log\,n)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
inline void fff(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
}
const int MOD=1e9+7;
const int inv2 = 5e8 + 4;
const int N=1e7+10;
int inv[233],C[233];
int n,q1,q2,q3,mxc;
LL ans1,ans2,ans3,ans4;
void solve1(){
	if(q1==0) ans1=n;
	else{
		for(int i=1;i*i<q1;i++) if(q1%i==0) ans1+=2;
		int tt=sqrt(q1);
		if(tt*tt==q1) ans1++;
	}
}
struct hash_table{
	static const int MOD=2333347;
	int hash(int _key1,int _key2){
		return (_key1+(_key2<<20))%MOD;
	}
	int hd[MOD],key1[N],key2[N],val3[N],val4[N],nxt[N],tl;
	int insert(int _key1,int _key2,int _val3,int _val4){
		int h=hash(_key1,_key2);
		++tl;
		key1[tl]=_key1;
		key2[tl]=_key2;
		val3[tl]=_val3;
		val4[tl]=_val4;
		nxt[tl]=hd[h];hd[h]=tl;
		return tl;
	}
	int findid(int _key1,int _key2){
		int h=hash(_key1,_key2);
		int cnt=1;
		for (int i=hd[h];i;i=nxt[i],++cnt){
			if (mxc<cnt) mxc=cnt;
			if (key1[i]==_key1&&key2[i]==_key2)
				return i;
		}
		return -1;
	}
}dp;
int dfs(int n,int d){
	int ret=dp.findid(n,d);
	if(ret!=-1) return ret;
	int dp3=0,dp4=0;
	if(d==1) return dp.insert(n,d,1,1);
	if(n==1) return dp.insert(n,d,0,0);
	for(int l=2,r=-1;l<=n;l=r+1){
		int t=n/l;r=n/t;
		int id=dfs(t,d-1);
		dp3=(dp3+(LL)dp.val3[id]*(r-l+1))%MOD;
		dp4=(dp4+(LL)dp.val4[id]*(l+r)%MOD*(r-l+1)%MOD*inv2)%MOD;
	}
	dp4=(dp4+dp3)%MOD;
	return dp.insert(n,d,dp3,dp4);
}
void solve34(){
	for(int i=1;i<=q3&&i<=25;i++){
		int id=dfs(n,i);
		ans3+=dp.val3[id];
		ans4+=dp.val4[id];
	}
	ans3%=MOD;ans4%=MOD;
}

void solve2(){
	inv[1]=C[0]=1;
	for(int i=2;i<=25;i++)
		inv[i]=(LL)(MOD-MOD/i)*inv[MOD%i]%MOD;
	for(int i=1;i<=25;i++)
		C[i]=(LL)C[i-1]*(q2-i+1)%MOD*inv[i]%MOD;
	for(int i=1;i<=q2&&i<=25;i++){
		int id=dfs(n,i);
		ans2=(ans2+(LL)dp.val3[id]*C[i])%MOD;
	}
}
int main(){
//	fff();
	scanf("%d%d%d%d",&n,&q1,&q2,&q3);
	ans1=0,ans2=0,ans3=0,ans4=0;
	solve1();solve34(),solve2();
	printf("%lld %lld %lld %lld",ans1,ans2,ans3,ans4);
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/82951677