【模拟赛】2019.9.5

背景:

终于,还是逃不过的模拟赛,(尽管自从从海亮的15天自闭模拟赛回来就没有再打过比赛,,,整个八月就是和线段树过了一个月,,,,),毕竟都快比赛了,,老师就说是一个星期至少两场的模拟赛,听说到后面还会加多,,但是就是不停课,,,(无语ing ┐( ̄ヮ ̄)┌)说是今年的形式比较严峻了,毕竟Noip都没了,尽管改成了CSP,但是很明显,教育局还是不好惹的,,,但是这还是没有妨碍着我好好“上课”的步伐(好吧,一个星期不上了不到三十节课就很,,了),尽管下星期的开学考,我,,表示丝毫不慌,,,毕竟就没怎么上过课,,,,
不瞎扯了,,,还是好好过来看看昨天的模拟赛吧,,,

题目:

(尽管在考试的时候老师发了简化题目,但是我还是读了一个半小时的题目,,结果就敲了个第二题的暴力60分,最后一个小时就认认真真推了个等差数列的T1假算法,结果就过了第一个样例,,,╥﹏╥)(这题目,,怎么就noip级别了,,,╥﹏╥)

T1:树上路径(phantasm)

简化题目:

求1,2,…,n有多少个长为m的子序列a,满足:

a 1 = 1 , a m = n a_1=1,a_m=n a1=1,am=n
∀ i , a i + 1 − a i > = k ∀i,a_{i+1}-a_i>=k i,ai+1ai>=k

保证这样的子序列存在。只需要判断方案数的奇偶性即可。

n , m , k < = 1 0 9 , T < = 2 × 1 0 6 n,m,k<=10^9,T<=2×10^6 n,m,k<=109,T<=2×106

题解:

这个题目啊,我就看了好长时间才看懂的,表示原题真的好容易糊弄人的!!还不如推一遍样例,,(本人认为推一变样例就是解释题面的最佳方案!)(仅仅是个人观点,说错了别打脸,,,)

先说一下所谓的暴力写法吧,这个题的暴力分给了45分的,30分的dfs,还有15分的特殊情况,(这样的话,加上我的60,Day1就过百了,,,菜死了),但是我还是花了一早上的时间研究了一下正解,,结果是组合数,,╥﹏╥(去***的等差数列)

  • 对于 m < = 3 m<=3 m<=3 的算法

    m = 2 m=2 m=2 时 , a n s = 1 ans=1 ans=1
    m = 3 m=3 m=3 时, a n s = n − 2 ∗ k ans=n-2*k ans=n2k

  • 对于 k = 1 k=1 k=1 的算法

    a n s = ( n − 2 m − 2 ) ans=\dbinom{n-2}{m-2} ans=(m2n2)

  • 对于 k < = 2 k<=2 k<=2 的算法
    a n s = ( n − m − 1 m − 2 ) ans=\dbinom{n-m-1}{m-2} ans=(m2nm1)

  • 引入dp思想的算法

  • f [ i ] [ j ] f[i][j] f[i][j]表示 a i = j a_i=j ai=j时,子序列a前i个位置可能的情况数(就是经过i位置到达j时的方案数)
    f [ i ] [ j ] = ∑ p = 1 i − k f [ i − 1 ] [ p ] f[i][j]=\sum_{p=1}^{i-k} f[i-1][p] f[i][j]=p=1ikf[i1][p]
    这里DP的复杂度就是 O ( T n m ) O(Tnm) O(Tnm)

    k < = 200 k<=200 k<=200时,只需做200遍DP就好这样就可以得65分了,复杂度就是 O ( T + n m k ) O(T+nmk) O(T+nmk)的了。

  • 推式子喽,,,(这里是需要两次差分来进行对于式子的简化)

    b i = a i + 1 − a i b_i=a_{i+1}-a_i bi=ai+1ai

    则设一个 b b b序列满足:

    1. ∀ i , b i ∀i,b_i i,bi [ k , n ] [k,n] [k,n]上的整数。
    2. f [ i ] [ j ] = ∑ i = 1 m − 1 b i = n − 1 f[i][j]=\sum_{i=1}^{m-1} b_i=n-1 f[i][j]=i=1m1bi=n1

    再设一个c序列来除掉第一个条件,这样就满足:

    1. ∀ i , c i ∀i,c_i i,ci是正整数
    2. ∑ i = 1 m − 1 c i = n − 1 − ( m − 1 ) ∗ ( k − 1 ) \sum_{i=1}^{m-1} c_i=n-1-(m-1)*(k-1) i=1m1ci=n1(m1)(k1)

由隔板法得:
a n s = ( n − 2 − ( m − 1 ) ∗ ( k − 1 ) m − 2 ) ans=\dbinom{n-2-(m-1)*(k-1)}{m-2} ans=(m2n2(m1)(k1))
这样的话就是90分的做法了,那100分的怎么做呢,,
——再加个Lucas定理不就好了嘛,,,,

由Lucas 定理, ( n k ) ≡ 1 ( m o d 2 ) \dbinom{n}{k}≡1 (mod 2) (kn)1(mod2) 当且仅当二进制下k 的各
位都不大于n 的对应位,即 n n n & k = k k=k k=k
这样的话就可以 O ( 1 ) O(1) O(1)求解了。

代码:
#include<bits/stdc++.h> 
using namespace std;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int T,n,m,k;
int main()
{
	freopen("phantasm.in","r",stdin);
	freopen("phantasm.out","w",stdout);
	T=read();
	for(int i=1;i<=T;i++)
	{
		n=read(); m=read(); k=read();
		if((((n-2)-(m-1)*(k-1))&(m-2))==(m-2)) puts("Yes"); else puts("No"); 
	}
	return 0;
}

T2:泳池(skylines)

简化题目:

给一个n个点的树,点有权c_i,边也有权。有q次询问,每次给定u,询问:
f ( u ) = m i n ( d i s ( u , v ) + c u − c v ) ( v ≠ u ) f(u)=min(dis(u,v)+c_u-c_v)(v \ne u) f(u)=min(dis(u,v)+cucv)(v=u)
n , q < = 2 × 1 0 5 n,q<=2×10^5 n,q<=2×105

题解:

这个题,我考场上就写了个60分的做法,就写了个 d i j k s t r a dijkstra dijkstra,没有想到正解,表示现在也没有看懂题解,即其代码,(这ACM选手的指针加大量头文件,,我服了)

代码::

(就直接放一下60分的暴力吧,尽管很简单,,)

#include<bits/stdc++.h>
#define int long long 
#define mkp make_pair
#define fir firsr 
#define sec second 
using namespace std;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const int ocean=2147483647;
const int sea=2e5+7;
struct see{int ver,next,edge;}e[sea<<1];
int n,tot,ans,res[sea],dis[sea],vis[sea],w[sea],last[sea];
priority_queue<pair<int ,int > >Q;
void add(int x,int y,int z){e[++tot].edge=z;e[tot].next=last[x],e[tot].ver=y;last[x]=tot;} 
void dijkstal(int s)
{
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;Q.push(mkp(0,s));
	while(Q.size())
	{
		int x=Q.top().sec;Q.pop();
		if(vis[x]) continue; vis[x]=1;
		for(int i=last[x];i;i=e[i].next)
		{
			int y=e[i].ver,z=e[i].edge;
			if(dis[y]>dis[x]+z)
			{
				dis[y]=dis[x]+z;
				Q.push(mkp(dis[y],y));
			}
		}
		if(x==s) res[x]=ocean;
		else res[x]=dis[x]+w[s]-w[x];
		ans=min(ans,res[x]);
	}
}
signed main()
{
	freopen("skylines.in","r",stdin);
	freopen("skylines.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read(),z=read();
		add(x,y,z); add(y,x,z);
	}
	int T=read();
	while(T--)
	{ 
		ans=ocean;
		memset(dis,0,sizeof(dis));
		memset(vis,0,sizeof(vis));
		int x=read();dijkstal(x);
		printf("%lld\n",ans);
	}
	return 0;
}

T3:空之轨迹(kiseki)

在这里说明一下下面的题解是翻老师所发过来的文件的,应该是出题人所写,在这里就当做我博客的内容,并没有请问老师,如若有不满或者警告,将立即改掉!

简化题目:

随机生成一个 m + 1 个数的数列,第一个数为 0, 生成第 i个数时,在前 i − 1 个数中等概率选择一个数 k, 则第 i 个数为k + 1。每个数均有一个对应的权值,求数列权值和的期望。

S S S:数列的权值和

X i X_i Xi:第i个数的权值( X 1 X_1 X1为定值0,所以忽略不计)
S = ∑ i = 2 m + 1 X i S=\sum_{i=2}^{m+1}X_i S=i=2m+1Xi

根据期望的线性性有(和的期望等于期望的和)
E ( S ) = ∑ i = 2 m + 1 E ( X i ) E(S)=\sum_{i=2}^{m+1}E(X_i) E(S)=i=2m+1E(Xi)
只要求出 E ( X i ) E(X_i) E(Xi)就可以了,再设 P [ i ] [ j ] P[i][j] P[i][j]:序列的第i个数为j的概率
E ( X i ) = ∑ j = 1 i − 1 P [ i ] [ j ] ∗ a j E(X_i)=\sum_{j=1}^{i-1}P[i][j]\ast a_j E(Xi)=j=1i1P[i][j]aj
现在只要求出 P [ i ] [ j ] P[i][j] P[i][j]就可以了 j j j的出现要求在前 i − 1 i-1 i1个数中随机到 j − 1 j-1 j1
P [ i ] [ j ] = ∑ k = 1 i − 1 1 i − 1 P [ k ] [ j − 1 ] P[i][j]=\sum_{k=1}^{i-1}\frac {1}{i-1}P[k][j-1] P[i][j]=k=1i1i11P[k][j1]
综上所述
E ( S ) = ∑ i = 2 m + 1 ∑ j = 1 i − 1 a j ∑ k = 1 i − 1 1 i − 1 P [ k ] [ j − 1 ] E(S)=\sum_{i=2}^{m+1}\sum_{j=1}^{i-1}a_j \sum_{k=1}^{i-1}\frac {1}{i-1}P[k][j-1] E(S)=i=2m+1j=1i1ajk=1i1i11P[k][j1]

代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int mod=998244353;
const int sea=1e5+7;
const int pool=30;
int n,m;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
LL p[pool][pool],a[sea],ny[pool],x[pool];
LL ksm(LL a,LL b)
{
	LL s=1;
	while(b)
	{
		if(b&1) s=s*a%mod;
		a=a*a%mod; b>>=1;
	}
	return s;
}
int main()
{
	freopen("kiseki.in","r",stdin);
	freopen("kiseki.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) a[i]=read();  
	for(int i=1;i<=m+1;i++) ny[i]=ksm(i,mod-2);
	x[1]=0; p[1][0]=1; 
	for(int i=2;i<=m+1;i++)
	for(int j=1;j<=i-1;j++)
	{
		for(int k=1;k<=i-1;k++)
		p[i][j]=(p[i][j]+p[k][j-1]*ny[i-1]%mod)%mod;
		x[i]=(x[i]+p[i][j]*a[j]%mod)%mod;
	}
	LL ans=0;
	for(int i=2;i<=m+1;i++) ans=(ans+x[i])%mod;
	printf("%lld\n",ans); 
	return 0; 
}

总结:

这次的模拟赛就是猝不及防啊,刷了一个月的线段树,数据结构,这就DP,和图论都快忘完了,还是要在敲敲板子复习复习啊,,,要不然就死人了,,,这次考了个组合数,树形DP,还有个不知道是什么知识点的算是期望的数学题,考的数学内容还是偏多的,这时候就想到了我的组合数(T▽T),早知道开数论的时候就顺便把组合数也开了,导致现在就是见到组合数比较慌,,,要好好补补了,,

Continue……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值