ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

模拟85 考试总结

2021-10-30 07:31:39  阅读:181  来源: 互联网

标签:return int sum long 模拟 ans const 考试 85


题目背景好评

考试经过

还是开题顺序有问题
目测T1不简单,T2是个什么贪心之类的,于是一头扎进T3开始推柿子,结果1.5h无果,然后心态崩掉
然后开始打暴力,T2搞了个一直排序的乱搞不知道有多少分,T1看出来是斯特林就有了40,T4拿个子任务跑路
剩半个小时不知道干啥,oj也不用查文件,于是瞎推了半天啥也没推出来
40+35+20+20=125,比预期高,虽然还是啥也不是

T1.莓良心

考场上想的是枚举每个人所在的组的大小,就有

\[ans=\sum_{w_i}\times (\sum_{i=0}^{n-k}(i+1)\dbinom{n-1}{i}{n-i-1 \brace k-1}) \]

然后发现不会算一列斯特林,就死了
正解是换了一种方法,他的意思是对于一组里的所有元素,每一对都会产生\(w_x+w_y\)的贡献,就是
image
第二个就是把1个和两个的分开算了,这个柿子会发现全程只用算一个斯特林
那么由斯特林公式得到

\[{n \brace k}=\frac{1}{k!}\sum_{i=0}^k (-1)^i\dbinom{k}{i}(k-i)^n \]

于是就能直接做了,公式学长讲课有讲,先背过吧

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int N=2050;
int sit[N][N],jc[N],n,k,a[N],inv[N],jcny[N];
inline int C(int x,int y)
{
	if(x<y)return 0;
	if(!y)return 1;
	return jc[x]*jcny[y]%mod*jcny[x-y]%mod;
}
signed main()
{
	freopen("ichigo.in","r",stdin);
	freopen("ichigo.out","w",stdout);
	sit[0][0]=jc[0]=1;
	for(int i=1;i<=2000;i++)
	{
	 	sit[i][0]=0;
	 	for(int j=1;j<=i;j++)
		 sit[i][j]=(sit[i-1][j-1]+j*sit[i-1][j]%mod)%mod;
		jc[i]=jc[i-1]*i%mod;
	}
	jc[1]=jcny[1]=jcny[0]=inv[1]=1;
	for(int i=2;i<=2000;i++)
	{
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		jcny[i]=jcny[i-1]*inv[i]%mod;
	}
	cin>>n>>k;int ans=0,sum=0;
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum=(sum+a[i])%mod;
	for(int i=0;i<=n-k;i++)ans=(ans+(i+1)*C(n-1,i)%mod*sit[n-1-i][k-1]%mod)%mod;
	ans=ans*sum%mod;cout<<ans<<endl;
	return 0;	
}

T2.尽梨了

发现应该按照\(a/b\)排序,这样最终选择的一定是他的一个子序列,更准确讲\(i\)在\(j\)前面满足\((b_i+1)a_j<(b_j+1)a_i\)
这个东显然可dp,\(f_{i,j}\)表示前\(i\)个买了\(j\)个的最少花费,发现当\(a>0\)他的花费指数增加,所第二维只开\(log\)
最后剩下一堆\(a=0\)的,扫过去判断还能买多少即可,总复杂度\(n\log n\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=200030;
struct t{int a,b;}p[N];
inline bool cmp(t x,t y)
{
	if((x.b+1)*y.a!=(y.b+1)*x.a)return (x.b+1)*y.a<(y.b+1)*x.a;
	return x.b<y.b;
}
int f[N][35];
signed main()
{
	freopen("eriri.in","r",stdin);
	freopen("eriri.out","w",stdout);
	int n,T;cin>>n>>T;
	for(int i=1;i<=n;i++)scanf("%lld%lld",&p[i].a,&p[i].b);
	sort(p+1,p+1+n,cmp);int ga=n+1;
	for(int i=1;i<=n;i++)if(!p[i].a){ga=i;break;}
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<ga;i++)
	{
		for(int j=0;j<=min(i,(int)30);j++)
		{
			if(f[i-1][j]<=T)f[i][j]=min(f[i][j],f[i-1][j]);
			if(f[i-1][j-1]<=T&&j)f[i][j]=min(f[i][j],f[i-1][j-1]+1+(f[i-1][j-1]+1)*p[i].a+p[i].b);
		}
	}
	int ans=0;
	for(int i=0;i<=30;i++)
	{
		if(f[ga-1][i]>T)break;int s=i,sum=f[ga-1][i];
		for(int j=ga;j<=n;j++)
		{
			sum+=p[j].b+1;
			if(sum>T)break;s++;
		}
		ans=max(ans,s);
	}		
	cout<<ans<<endl;
	return 0;
}

T3.团不过

卡农的弱化版,思路基本一样,设\(f_i\)表示\(i\)堆先手必败方案数,\(p_i\)表示堆总方案数
题解写的贼清楚,所以直接复制了
image
关键在于能看出来这个是个卡农,否则怎么都白搭

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1e7+10;
int p[N],f[N],n;
inline int ksm(int x,int y)
{
   int s=1;x%=mod;
   for(;y;y>>=1)
   {
      if(y&1)s=s*x%mod;
      x=x*x%mod;
   }
   return s;
}
signed main()
{
	freopen("yui.in","r",stdin);
	freopen("yui.out","w",stdout);
	cin>>n;int ga=ksm(2,n);
	p[0]=1;for(int i=1;i<=n;i++)p[i]=p[i-1]*(ga-i)%mod;
	f[1]=f[2]=0;
	for(int i=3;i<=n;i++)f[i]=(p[i-1]-f[i-1]+mod-(ga-i+1+mod)%mod*(i-1)%mod*f[i-2]%mod+mod)%mod;
	cout<<(p[n]-f[n]+mod)%mod<<endl;
	return 0;
}

T4.七负我

如果两个点之间没有连边,那么在他们分配的权值和确定的的情况下,一个分0一定不劣
那么就找图中的最大团就行了,然后权值平均分配
可以状压折半,预处理前一半的所有子集的最大团,枚举后一半的团(要\(check\)一下是团),看他的出边与前一般的交集\(s\),那当前团的点数\(i\)加上\(f_s\)就是答案,然后取最大的即可,实现比较简单

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2000;
struct node{
	int from,to,next;
}a[2*N];
int head[N],mm=1;
inline void add(int x,int y)
{
	a[mm].from=x;a[mm].to=y;
	a[mm].next=head[x];head[x]=mm++;
}
int n,m,sum;
inline int getsum(int x)
{
	int s=0;
	while(x)
	{	
		if(x&1)s++;
		x>>=1;
	}
	return s;
}
vector <int> sta[22];
int f[1ll<<22],to[45],t,tt,v[45][45];
inline bool check(int s)
{
	for(int i=1;i<=tt;i++)
	{
		if(!((s>>(i-1))&1))continue;
		int x=i+t;
		for(int j=1;j<=tt;j++)
		{
			if(!((s>>(j-1))&1))continue;
			if(j==i)continue;
			if(!v[i+t][j+t])return 0;
		}
	}
	return 1;
}
signed main()
{
	freopen("nanami.in","r",stdin);
	freopen("nanami.out","w",stdout);
	cin>>n>>m>>sum;t=n/2;
	for(int i=1;i<=m;i++)
	{
		int x,y;scanf("%lld%lld",&x,&y);
		add(x,y);add(y,x);
		if(x<=t)to[y]|=1ll<<(x-1);
		if(y<=t)to[x]|=1ll<<(y-1);
		v[x][y]=v[y][x]=1;
	}
	for(int i=1;i<(1ll<<t);i++)sta[getsum(i)].push_back(i);
	for(int i=1;i<=t;i++)f[1ll<<(i-1)]=1;
	for(int i=1;i<=t;i++)
	{
		for(int j=0;j<sta[i].size();j++)
		{
			int s=sta[i][j];
			for(int k=1;k<=t;k++)
			{
				if((s>>(k-1))&1)continue;
				if((to[k]&s)==s&&f[s]==i)f[s|(1ll<<(k-1))]=max(f[s|(1ll<<(k-1))],f[s]+1);
				else f[s|(1ll<<(k-1))]=max(f[s|(1ll<<(k-1))],f[s]);
			}				
		}
	}
	int ans=0;tt=n-t;
	for(int i=1;i<(1ll<<t);i++)ans=max(ans,f[i]);
	for(int i=1;i<(1ll<<tt);i++)
	{
		if(!check(i))continue;
		int s=(1ll<<t)-1;
		for(int j=1;j<=tt;j++)
		{
			if((i>>(j-1))&1)s&=to[j+t];
		}
		ans=max(ans,getsum(i)+f[s]);
	}
	printf("%.6lf\n",1.0*ans*(ans-1)*sum*sum/(2.0*ans*ans));
	return 0;
}

考试总结

感觉最近陷进这样的怪圈中:

想题,想了半天,然后假掉
两条路:
继续,然后爆零,然后垫底
换题,然后暴力,然后垫底
有时我选择前者,因为我太懦弱,没有力挽狂澜的勇气
有时我选择后者,因为我太懒惰,没有绝处逢生的毅力

于是嘲笑自己,然后又是一天过去
希望自己想明白一些事情吧,毕竟时间不多了

标签:return,int,sum,long,模拟,ans,const,考试,85
来源: https://www.cnblogs.com/PMZG/p/15484219.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有