修仙录 3.16

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41709770/article/details/88602670

今天的题感觉好降智。

不过能改完也是很满意的


计算

http://115.236.49.52:83/contest/1126/problem/1
不是很难的数位dp
问题在于我竟然不知道有exp这种东西。。。
定义f[i][j][x][y]:处理到第i位,与m匹配到j位,是否已经保证小于n,是否是前导0
只要预处理e的各个次方,再用个kmp处理j,转移就好了
注意答案最后要减去全零的1就行

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=15;

int n,m;
int num1[MAXN],l1,num2[MAXN],l2;
int next[MAXN];
double w[MAXN][MAXN],f[MAXN][MAXN][2][2],ans;

void prework(){
	for(int i=2,j=0;i<=l2;i++){
		while(j&&num2[j+1]!=num2[i]) j=next[j];
		if(num2[j+1]==num2[i]) j++;
		next[i]=j;
	}
	for(int i=0,x=1;i<=l1;i++,x*=10) for(int j=0;j<=9;j++) w[j][i]=exp((double)x*j/n);
}

int kmp(int i,int x){
	while(i&&num2[i+1]!=x) i=next[i];
	if(num2[i+1]==x) i++;
	return i;
}

int main(){
	cin>>n>>m; if(!m) l2=1;
	for(int i=n;i;i/=10) num1[++l1]=i%10; reverse(num1+1,num1+1+l1);
	for(int i=m;i;i/=10) num2[++l2]=i%10; reverse(num2+1,num2+1+l2);
	prework();
	f[0][0][1][1]=1;
	for(int i=0;i<l1;i++) for(int j=0;j<l2;j++)
	for(int x=0;x<2;x++) for(int y=0;y<2;y++){
		if(!f[i][j][x][y]) continue;
		int lim= x?num1[i+1]:9;
		for(int a=0;a<=lim;a++){
			int to=kmp(j,a),q=y&(!a);
			if(to==l2&&(m||!q)) continue;
			if(!m&&q) to=0;
			int p= x&&a==lim;
			f[i+1][to][p][q]+=f[i][j][x][y]*w[a][l1-i-1];
		}
	}
	for(int i=0;i<l2;i++) for(int x=0;x<2;x++) for(int y=0;y<2;y++) ans+=f[l1][i][x][y];
	printf("%.3lf",ans-1);
	return 0;
}

移动

http://115.236.49.52:83/contest/1126/problem/2
贪心贪错只有10分。
正确的贪心有点鬼。
这是环形老鼠进洞问题(突然想起冬令营。)
我们用网络流辅助分析
每个点可以向其他点传权值,可以看成有一条带正权值的边,和一条带负权值的反边
而且可以发现,这个环上的所有边是可以同时加减的
那么我们一定可以使某两个点之间的边值为0,即可以在这里断环为链
而剩下的权值显然都向中间传是最优的
结果代码短的可以

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+5;

int n,a[MAXN],sum[MAXN];
long long ans;

int main(){
	cin>>n;
	memset(sum,-1,sizeof(sum));
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[a[i]]++;
	for(int i=2;i<=n;i++) sum[i]+=sum[i-1];
	sort(sum+1,sum+1+n);
	for(int i=1;i<=n;i++) ans+=1ll*abs(sum[i]-sum[(n+1)/2]);
	cout<<ans;
	return 0;
}

分离

http://115.236.49.52:83/contest/1126/problem/3
这道题简直承包了降智的一切。
考场打表骗分就算了,结果正解就是打表。
发现96是无论如何都放不进集合的,即从96开始以后的答案都是0.。。
用dfs把96之前的答案跑出来就行,随便剪剪枝,5个小时还跑不完吗?

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=1e5+5;

int T,n,M;
long long ans[]={0, 2, 4, 8, 16, 32, 48, 96, 144, 288, 432, 864, 960, 1920, 2880, 
4320, 7296, 14592, 16512, 33024, 34368, 53120, 79680, 159360, 111360, 
222720, 334080, 541440, 685440, 1370880, 887040, 1774080, 2200320, 3655680, 5483520, 
8232192, 10851840, 21703680, 32555520, 54259200, 73958400, 147916800, 75340800, 150681600, 207636480, 
355000320, 532500480, 1065000960, 348364800, 696729600, 952197120, 1586995200, 2221793280, 4443586560, 2113413120,
3808788480, 3901685760, 6502809600, 9754214400, 19508428800, 9057484800, 18114969600, 27172454400, 42268262400, 63402393600,
117747302400, 54344908800, 108689817600, 152165744640, 253609574400, 398529331200, 797058662400, 489104179200, 978208358400, 1467312537600, 
2445520896000, 3423729254400, 5579410636800, 2575112601600, 5150225203200, 2575112601600, 5150225203200, 7725337804800, 15450675609600, 7023034368000, 13042778112000, 
19564167168000, 32606945280000, 26085556224000, 52171112448000, 34780741632000, 57967902720000, 81155063808000, 135258439680000, 202887659520000, 376791367680000};

int main(){
	cin>>T;
	while(T--){
		scanf("%d%d",&n,&M);
		if(n<96) printf("%lld\n",ans[n]%M);
		else printf("0\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41709770/article/details/88602670
今日推荐