JZOJ 4742 单峰
题目
问 1 ∼ n 1\sim n 1∼n的排列中有多少个单峰排列
分析
对于 n − 1 n-1 n−1, n n n的位置只能在 n − 1 n-1 n−1的左右,所以也就是 2 n − 1 2^{n-1} 2n−1
代码
#include <cstdio>
#define rr register
using namespace std;
typedef long long ll;
const ll WYC=1000000007;
inline ll modd(ll ans,ll mod){
rr int f=1;
if (ans<0) ans=-ans,f=-f;
if (ans>=(mod<<4)) ans=(ans-(mod<<4))%mod;
else{
ans-=(ans>=(mod<<3))*(mod<<3);
ans-=(ans>=(mod<<2))*(mod<<2);
ans-=(ans>=(mod<<1))*(mod<<1);
ans-=(ans>=(mod<<0))*(mod<<0);
}
return ans*f;
}
inline ll ksm(ll x,ll y){
rr ll ans=1;
for (;y;y>>=1,x=modd(x*x,WYC))
if (y&1) ans=modd(ans*x,WYC);
return ans;
}
signed main(){
rr ll n; scanf("%lld",&n);
printf("%lld",ksm(2,modd(n-1,WYC-1)));
return 0;
}
JZOJ 4743 积木
题目
搭积木必须让上一层底面对于该层完全覆盖(没有凸出来的),问最多搭多高
分析
状压dp,设 d p [ i ] [ n ] [ 0 ∼ 3 ] dp[i][n][0\sim 3] dp[i][n][0∼3]表示当前层用的是第 n n n块积木哪一个面朝上时的最大高度,转移时用未在集合内的积木。
代码
#include <cstdio>
#include <cctype>
#define rr register
#define max(a,b) ((a)>(b)?(a):(b))
#define check(xx1,yy1,xx2,yy2) ((xx1>=xx2&&yy1>=yy2)||(xx1>=yy2&&yy1>=xx2))
using namespace std;
struct rec{int x,y,z;}a[16];
int n,all,k,dp[32768][16][3],ans;
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(){
all=1<<(n=iut());
for (rr int i=0;i<n;++i) a[i]=(rec){iut(),iut(),iut()};
for (rr int i=0;i<all;++i)
for (rr int j=0;j<n;++j){
if (i&(1<<j)) continue;
rr int now=i|(1<<j);
for (rr int p=0;p<n;++p)
for (rr int t=0,ch,ku;t<3;++t){
if (!t) ch=a[p].x,ku=a[p].y;
else if (t&1) ch=a[p].x,ku=a[p].z;
else ch=a[p].y,ku=a[p].z;
if (check(a[j].x,a[j].y,ch,ku)) dp[now][j][0]=max(dp[now][j][0],dp[i][p][t]+a[j].z);
if (check(a[j].x,a[j].z,ch,ku)) dp[now][j][1]=max(dp[now][j][1],dp[i][p][t]+a[j].y);
if (check(a[j].y,a[j].z,ch,ku)) dp[now][j][2]=max(dp[now][j][2],dp[i][p][t]+a[j].x);
}
}
for (rr int i=0;i<=n;++i)
for (rr int t=0;t<3;++t) ans=max(ans,dp[all-1][i][t]);
return !printf("%d",ans);
}
JZOJ 4745 看电影
题目
N
N
N个人排成一圈,按顺时针顺序标号为
1
∼
N
1\sim N
1∼N,每次随机一个
1
∼
N
1\sim N
1∼N的编号,假设随机到的编号是
X
X
X,如果编号为
X
X
X人还未踢出,则将这个人踢出,否则看编号为
X
X
X
m
o
d
mod
mod
N
+
1
N+1
N+1(即顺时针顺序下一个编号)的人是否存活,如果还未踢出则将他踢出,否则继续看编号
(
X
+
1
)
(X+1)
(X+1)
m
o
d
mod
mod
N
+
1
N +1
N+1的人,如果已被踢出看顺时针的下一个,以此类推,直到踢出一个人为止。重复上述操作,直到剩下
K
K
K个人。
已知小S的编号是
I
d
Id
Id,问按照小S的方法来他有多少的概率可以不被踢出,成功得到看电影的机会。
分析
由于这个东西是随机的,通过一些玄学的打表东西可以发现每个人的概率应该是一样的,所以就是
k
n
\frac{k}{n}
nk
代码
#include <cstdio>
#define rr register
using namespace std;
int n,k,t;
inline signed gcd(int a,int b){return b?gcd(b,a%b):a;}
signed main(){
scanf("%d%d%*d",&n,&k);
if (!k) printf("0/1");
else t=gcd(k,n),printf("%d/%d",k/t,n/t);
return 0;
}