Noip模拟题解题报告

Pro

题目链接

Sco

预计得分: 100 + 20 + 30 = 150 100 + 20 + 30 = 150 100+20+30=150

实际得分: 100 + 20 + 30 = 150 100 + 20 + 30 = 150 100+20+30=150

Sol

number

很裸的模拟题

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int len , flag , sm1 , sm2;
string d1 , d2; 

int main() {
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);
	cin>>d1>>d2;
	len = d1.length();
	for(int i=0; i<len; i++) {
		if(d1[i] < d2[i]) {
			d1[i] = '#';
			sm1++;
		}
		if(d1[i] > d2[i]) {
			d2[i] = '#';
			sm2++;
		}
	}
	if(sm1==len)
		printf("BOOM");
	else {
		flag = 0;
		for(int i=0; i<len; i++) {
			if((!flag&&d1[i]=='0')||d1[i]=='#')
				continue;
			flag = 1;
			cout<<d1[i];
		}
		if(!flag)
			printf("0");
	}
	printf("\n");
	if(sm2==len)
		printf("BOOM");
	else {
		flag = 0;
		for(int i=0; i<len; i++) {
			if((!flag&&d2[i]=='0')||d2[i]=='#')
				continue;
			flag = 1;
			cout<<d2[i];
		}
		if(!flag)
			printf("0");
	}
	return 0;
} 

merge

打了60的DP……

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 1000000000000000000
using namespace std;

long long n , cnt[1005] , sum[1005] , dp[1005][1005] , ans = INF;
inline long long mymin(long long a , long long b) { return a<b?a:b; }
inline long long mymax(long long a , long long b) { return a>b?a:b; }

int main() {
	freopen("merge.in","r",stdin);
	freopen("merge.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1; i<=n; i++) {
		scanf("%lld",&cnt[i]);
		cnt[i+n] = cnt[i];		
	}
	n = 2 * n;
	for(int i=1; i<=n; i++)
		sum[i] = sum[i-1] + cnt[i];
	for(int j=1; j<=(n-1)/2; j++)
		for(int i=1; i<=n; i++) { 
			int len = 2*j+1;
			dp[i][i+len-1] = INF;
			if(len==3) {
				dp[i][i+len-1] = mymin(dp[i][i+len-1] , sum[i+len-1]-sum[i-1]);
				continue;
			}
			for(int k1=0; i+2*k1+1<=i+len-1; k1++) 
				for(int k2=0; i+2*k1+1+2*k2+1<=i+len-1; k2++) {
					int len1 = 2*k1+1 , len2 = 2*k2+1;
					if((len-len1-len2)%2)
						dp[i][i+len-1] = mymin(dp[i][i+len-1] , dp[i][i+len1-1]+dp[i+len1][i+len1+len2-1]+dp[i+len1+len2][i+len-1]+sum[i+len-1]-sum[i-1]);
				}
			} 
	printf("%lld",dp[1][n/2]);
	return 0;
}

problem

30分是爆搜分

#include<iostream>
#include<cstdio>
#define mod 1000000007
using namespace std;

int n , m , num[25] , vis[25];
long long ans;

void dfs(int opt) {
	if(opt==n) {
		ans = (ans+1)%mod;
		return ;
	}
	if(num[opt+1])
		dfs(opt+1);
	else {
		for(int i=1; i<=n; i++)
			if(!vis[i]&&i!=opt+1) {
				vis[i] = 1;
				dfs(opt+1);
				vis[i] = 0;
			}
	}	
}

int main() {
	freopen("problem.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++) {
		int x , y;
		scanf("%d%d",&x,&y);
		num[y] = x;
		vis[x] = 1;
	}
	for(int i=1; i<=n; i++)
		if(!num[i]) {
			for(int j=1; j<=n; j++)
				if(!vis[j]&&j!=i) {
					vis[j] = 1;
					dfs(i);
					vis[j] = 0;				
				}
			printf("%d",ans%mod);
			return 0;
		}
	return 0;
}

数据范围跨度好大,正解是数学做法:

按照容斥原理可得,答案 a n s = ∑ i = 0 s ( − 1 ) i C s i ( n − m − k ) ! ans=\sum_{i=0}^s (-1)^i C_{s}^{i}(n-m-k)! ans=i=0s(1)iCsi(nmk)!

其中s表示剩下n-m个数中有s个数可能放到原来的位置。

费马小定理求逆元,预处理阶乘,求组合数什么的胡乱搞搞

#include<iostream>
#include<cstdio>
#define mod 1000000007
using namespace std;

int n , m;
long long jc[100005] , ans , s , p1[100005] , p2[100005];

inline long long mypow(long long a , long long b) {
	if(!b)
		return 1;
	long long t = mypow(a , b/2)%mod;
	if(b&1)
		return ((t*t%mod)*a)%mod;
	return t*t%mod;
}

inline long long inv(long long x) {
	return mypow(x , mod-2)%mod;
}

void init() {
	jc[1] = 1;
	for(int i=2; i<=n; i++)
		jc[i] = (jc[i-1]*i)%mod;
	for(int i=1; i<=n; i++)
		if(!p1[i]&&!p2[i])
			s++;
	ans = jc[n-m];
}

long long C(long long x , long long y) {
	if(x<y||x==0)
		return 0;
	if(x==y)
		return 1;
	return jc[x]*inv(jc[x-y])%mod*inv(jc[y])%mod;
}

int main() {
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++) {
		int x , y;
		scanf("%d%d",&x,&y);
		p1[x] = 1;
		p2[y] = 1;
	}
	init();
	for(int i=1; i<=s; i++) {
		int t1 = C(s,i);
		if(i&1)
			ans = (ans-C(s,i)*jc[n-m-i]%mod+mod)%mod;
		else
			ans = (ans+C(s,i)*jc[n-m-i]%mod)%mod;
	}
	printf("%lld",ans%mod);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值