2019.05.25 【NOIP提高组】模拟 A 组

41 篇文章 0 订阅
21 篇文章 0 订阅

JZOJ 4786 小a的强迫症

题目

n n n种颜色的珠子,第 i i i种颜色的珠子有 a i a_i ai个,问有多少种排列,满足第 i i i种颜色的最后一个珠子,必然在第 i + 1 i+1 i+1种颜色的最后一个珠子的前面


分析

考虑倒序,对于每种排列,把最后一颗珠子放在最后一位,剩下的依照组合随意放置,那么一层层剥开,设 s i = ∑ j = 1 i a i s_i=\sum_{j=1}^i a_i si=j=1iai答案即为 ∑ i = 2 n C s i − 1 a i − 1 \sum_{i=2}^{n}C_{s_i-1}^{a_i-1} i=2nCsi1ai1


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int mod=998244353;
int n,a[100001],sum,inv[500001],fac[500001],ans=1;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
signed main(){
	n=iut(); inv[0]=fac[0]=inv[1]=fac[1]=1;
	for (rr int i=1;i<=n;++i) a[i]=iut(),sum+=a[i];
	for (rr int i=2;i<=sum;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for (rr int i=2;i<=sum;++i) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
	for (rr int i=n;i>1;--i)
		ans=1ll*ans*fac[sum-1]%mod*inv[a[i]-1]%mod*inv[sum-a[i]]%mod,sum-=a[i];
	return !printf("%d",ans);
} 

JZOJ 4787 数格子

题目

2 × 1 2\times 1 2×1的骨牌完全覆盖 4 × n 4\times n 4×n的棋盘,问共有多少种方案


分析

类比于POJ 2411 Mondriaan’s Dream,可以建立一个状压的矩阵,然而通项公式,可以得到 F n = F n − 1 + 5 × F n − 2 + F n − 3 − F n − 4 F_n=F_{n-1}+5\times F_{n-2}+F_{n-3}-F_{n-4} Fn=Fn1+5×Fn2+Fn3Fn4,然而我证明不出来,但是一波矩阵乘法就可以了


代码

#include <cstdio>
#include <cstring>
#define rr register
using namespace std;
struct maix{int p[4][4];}A,ANS;
int n,mod;
inline maix mul(maix A,maix B){
    rr maix C;
    for (rr int i=0;i<4;++i)
    for (rr int j=0;j<4;++j) C.p[i][j]=0;
    for (rr int i=0;i<4;++i)
    for (rr int j=0;j<4;++j)
    for (rr int k=0;k<4;++k)
    C.p[i][j]=(1ll*A.p[i][k]*B.p[k][j]+C.p[i][j])%mod;
    return C;
}
signed main(){
	while (1){
		memset(ANS.p,0,sizeof(ANS.p));
		memset(A.p,0,sizeof(A.p));
		scanf("%d%d",&n,&mod);
		if (!n&&!mod) return 0;
		if (n==1) {printf("1\n"); continue;}
		if (n==2) {printf("5\n"); continue;}
		if (n==3) {printf("11\n"); continue;}
		ANS.p[0][0]=ANS.p[1][1]=1,ANS.p[2][2]=1,ANS.p[3][3]=1;
		A.p[0][3]=-1,A.p[1][0]=1,A.p[2][1]=1,A.p[3][2]=1,A.p[2][3]=5,A.p[1][3]=1,A.p[3][3]=1;
		for (n-=3;n;n>>=1,A=mul(A,A))
		    if (n&1) ANS=mul(ANS,A);
		rr int ans[4]={0,0,0,0},h[4]={1,1,5,11};
		for (rr int j=0;j<4;++j)
		for (rr int k=0;k<4;++k)
		    ans[j]=(1ll*h[k]*ANS.p[k][j]+ans[j])%mod;
		printf("%d\n",ans[3]);
	}
}

JZOJ 4788 序列

题目

n n n 0 ∼ 3 0\sim 3 03数,可以选取一段区间加1,当变成4时自动变成0,问最少操作多少次使这 n n n个数变成另外 n n n 0 ∼ 3 0\sim 3 03的数


分析

类比于洛谷 1969 积木大赛,如果没有变成0的限制,答案即为
∑ max ⁡ { 0 , a i − a i − 1 } \sum \max\{0,a_i-a_{i-1}\} max{0,aiai1}
为什么呢,根据贪心,假设 a i ≥ a i − 1 a_i\geq a_{i-1} aiai1,那么就要再增加 a i − a i − 1 a_i-a_{i-1} aiai1,倘若小于,那么答案显然是不需要增加的
然而,这道题是加强版,那怎么做呢,区间 [ i ∼ j ] [i\sim j] [ij]需要取模当且仅当 a j − a j − 1 + 4 ≤ a i + 1 − a i a_j-a_{j-1}+4\leq a_{i+1}-a_{i} ajaj1+4ai+1ai,所以说需要统计个数,再求解


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int a[100001];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
signed main(){
	for (rr int t=iut();t;--t){
		rr int n=iut(),ans=0,lazy[5]={0,0,0,0,0};
		for (rr int i=1;i<=n;++i) a[i]=iut();
		for (rr int i=1;i<=n;++i) a[i]=(iut()-a[i]+4)&3;
		for (rr int i=1;i<=n;++i) ans+=a[i]>a[i-1]?a[i]-a[i-1]:0;
		for (rr int i=2;i<=n;++i){
			rr int now=a[i]-a[i-1];
			if (now>0){
				if (lazy[1]&&now>1) --lazy[1],++lazy[now],ans-=now-1;
				else if (lazy[2]&&now>2) --lazy[2],++lazy[now],ans-=now-2;
			}else ++lazy[now+4];
		}
		printf("%d\n",ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值