zoukankan      html  css  js  c++  java
  • test20190816 NOIP2019 模拟赛

    100+100+20=220,T3吐槽:整个考室没有一个人正确地理解了题意。

    树上路径(phantasm)

    Akari 的学校的校门前生长着一排 n 棵树,从西向东依次编号为 1 ∼ n。相邻两棵树间的距离 都是 1。

    Akari 上课的教学楼恰好在树 1 旁,所以每个课间,Akari 都很想走出教室,上树活动。Akari会依次经过 m 棵树,从树 1 一路向东跳到树 n。临近上课时,Akari 会再次上树,经过 m 棵树从树n 一路向西跳到树 1 ,准备上课。由于 Akari 睡眠很充足,Akari 每次跳跃至少会移动 k 的距离, 因此 Akari 在上树前需要合理规划她的跳跃路线。我们称每次上树过程中 Akari 跳过的全部 m 棵 树(包含树 1 和树 n)的集合为一条树上路径。

    Akari 喜欢按不同的顺序观察各种树木,因此她每次上树时选择的树上路径不会与之前选择过 的重复。这意味着,Akari 不会选择之前的课间选过的树上路径,且在从树 n 跳回树 1 时,也不会 沿这次跳到树 n 的树上路径原路返回。

    如果一次课间开始时,Akari 找不到符合条件的树上路径,那么她从此会放弃上树活动,开始 专心学习。如果一次课间即将即将结束时,Akari 还在树 n 且找不到符合条件的树上路径回到树 1, 她就会十分沮丧,选择逃课。

    请你帮助 Akari 判断,她是否会在某个课间选择逃课。

    【输入格式】

    从文件 phantasm.in 中读入数据。 每个测试点可能包含多组数据。第一行一个正整数 T ,表示数据组数。 每组数据包括一行三个正整数 n, m, k,含义见题目描述。

    【输出格式】

    输出到文件 phantasm.out 中。

    对于每组数据,输出一行一个字符串。如果 Akari 会逃课,输出 Yes ,否则输出 No 。

    . . . . . . . . . . . .

    【样例 1 输入】

    3
    10 3 2
    5 3 1
    15 5 3

    【样例 1 输出】
    No Yes No

    【样例 1 解释】

    第一组数据中,除了起点和终点外,合法的树上路径只能经过 3, 4, 5, 6, 7, 8 这 6 棵树,所以合 法的树上路径只有 6 种,经过 3 个课间后 Akari 就会停止上树活动。

    第二组数据中,合法的树上路径有 3 种,Akari 会在第 2 个课间结束时逃课。 第三组数据中,合法的树上路径有 10 种。

    【样例 2 输入】

    4

    15 4 3
    15 4 4
    15 5 3
    16 3 7

    【样例 2 输出】

    Yes No No No

    【样例 3】

    见选手目录下的 phantasm/phantasm3.inphantasm/phantasm3.ans

    【子任务】

    测试点 n m k T
    1 ≤ 10 ≤ 10 ≤ 10 ≤ 10
    2 ≤ 16 ≤ 16 ≤ 16 ≤ 102
    3
    4 ≤ 18 ≤ 18 ≤ 18
    5
    6
    7 ≤ 5, 000 ≤ 5, 000 ≤ 1 ≤ 5, 000
    8 ≤ 3 ≤ 5, 000
    9 ≤ 5, 000 ≤ 2
    10 ≤ 200 ≤ 200 ≤ 200 ≤ 200
    11
    12 ≤ 5, 000
    13
    14 ≤ 5, 000 ≤ 5, 000 ≤ 5, 000
    15
    16 ≤ 2 × 106
    17
    18
    19 ≤ 109 ≤ 109 ≤ 109
    20

    对于所有数据,2 ≤ n, m ≤ 109 , 1 ≤ k ≤ 109 , 0 ≤ T ≤ 2 × 106 ,保证第一个课间 Akari 从树 1 跳到

    n 的符合条件的树上路径存在。

    【提示】

    本题的输入数据可能很大,请避免使用过于缓慢的读入方式。

    题解

    首先把那个点数化成长度,然后不同的长度拆分就是不同的方案。

    那么答案就是有下界的拆分,直接套公式(或者像我一样手动构造)即可。

    [ans=inom{(n-1)-(m-1)(k-1)-1}{(m-1)-1} mod 2 ]

    使用卢卡斯定理即可。时间复杂度(O(T log v))

    bool binom(LL n,LL m){
    	if(n<m) return 0;
    	if(m==0) return 1;
    	return (n&1)>=(m&1) && binom(n>>1,m>>1);
    }
    int main(){
    	freopen("phantasm.in","r",stdin),freopen("phantasm.out","w",stdout);
    	for(int T=read<int>();T--;){
    		LL n=read<LL>(),m=read<LL>(),k=read<LL>();
    		puts(binom(n-1-(k-1)*(m-1)-1,m-1-1)?"Yes":"No");
    	}
    	return 0;
    }
    

    利用二进制的优良性质,可以将单次操作化为(O(1))的,参见std

    int ks = io;
    while (ks--) {
        int n = io, m = io, k = io;
        io << ((((n - 2 - (m - 1) * (k - 1)) & (m - 2)) == m - 2) ? "Yes
    " : "No
    ");
    }
    

    话说回来,我好像不会证明卢卡斯定理,放一个图

    非常的有道理啊。

    另外既然我都看到((1+x)^p equiv x^p + 1 mod p),那我就说一下今天讨论出来的多项式快速幂做法。

    若保证(f_0=1),则(f^k(x)=f^{kmod p}(x)mod x^n)

    证明大概就是有组合数的项因为卢卡斯定理变成(0)了,而剩下的是(x^{kp},k=1sim n-1)(1)。但是我们要对(x^n)取模,所以那些有(x)的项都被消掉了。所以(f^p(x)=1 mod x^n)

    泳池(skylines)

    【题目描述】

    小 A 的城市里有 n 座工厂,编号分别为 1 ∼ n。工厂间连有 n − 1 条. . 管道,形成一个无向. . 图,其中每条管道都有一定的长度,连接在两座不同的工厂间。

    每座工厂都装有废水处理设施,工厂 i 的蓄水量记为 ci 。由于工厂规模有限,工厂产生的废水必须经由管道输送到. 一座工厂进行处理。

    工厂 u 将废水输送到工厂 v 处理时,所需的. . . . 等于无向图中 u, v 间最短路径的长度,并且会产生 cucv. . . . (可能为负)。总成本等于运输成本与额外成本的和。

    为了降低污染,在接下来的 q 天内,每一天只有一座工厂会产生废水。你需要确定这座工厂将废水输送到哪一座工厂进行处理,可使得总成本最小。由于选择可能不唯一,你只需输出最小的总 成本。

    【输入格式】

    从文件 skylines.in 中读入数据。 第一行一个正整数 n

    第二行 n 个正整数 ci

    下接 n − 1 行,每行三个正整数 u, v, w,表示一条双向管道两端工厂的编号及长度。 第 n + 2 行一个正整数 q

    下接 q 行,每行一个正整数 x,表示这一天进行生产的工厂的编号。

    【输出格式】

    输出到文件 skylines.out 中。

    输出 q 行,每行一个整数,表示这一天总成本的最小值。

    【样例 1 输入】

    5

    7 7 6 9 9
    2 5 5
    2 3 1
    4 1 1
    1 2 2

    4
    2
    5
    3
    4

    【样例 1 输出】

    1
    7
    0
    3

    【样例 1 解释】

    第 1 天,工厂 2 输送到工厂 4 是一种最优方案,成本为 3 + (−2) = 1。
    第 2 天,工厂 5 输送到工厂 2 是一种最优方案,成本为 5 + 2 = 7。
    第 3 天,工厂 3 输送到工厂 2 是一种最优方案,成本为 1 + (−1) = 0。
    第 4 天,工厂 4 输送到工厂 1 是一种最优方案,成本为 1 + 2 = 3。

    【样例 2】

    见选手目录下的 skylines/skylines2.inskylines/skylines2.ans

    【样例 3】

    见选手目录下的 skylines/skylines3.inskylines/skylines3.ans

    【样例 4】

    见选手目录下的 skylines/skylines4.inskylines/skylines4.ans

    【子任务】

    测试点 n, q
    1 ≤ 10
    2 ≤ 200
    3 ≤ 2, 000
    4
    5
    6
    7 ≤ 2 × 105
    8
    9
    10

    对于表格中“链”为“是”的数据,保证所有管道满足 v = u + 1 且以 u 递增的顺序输入。

    对于所有数据,2 ≤ n ≤ 2 × 105 , 1 ≤ q ≤ 2 × 105 , 1 ≤ u, v, xn, 1 ≤ w ≤ 5000, 1 ≤ ci ≤ 108

    题解

    显然这是一道离线的问题。

    开始像“情报中心”那样推式子

    [ans_u=min_{v eq u} { dist_{u,v} + c_u - c_v }\ =min_{v eq u} { dep_u+dep_v-2dep_{lca} + c_u -c_v } ]

    确定一个根后,能作为(u)(lca)的只可能是(u)的祖先,所以这就变成了统计子树内部(dep_v-c_v)的睿智题。注意支持换根需要记录最优次优解,或者像我一样记录儿子的值来搞序列问题。

    时间复杂度(O(n+q))

    co int N=200000+10;
    co LL INF=1e18;
    int n;LL c[N];
    vector<int> to[N];vector<LL> we[N];
    LL dep[N],g[N],f[N]; // g: x itself not included
    vector<LL> son[N],pre[N],suf[N];
    
    void dfs1(int x,int fa){
    	f[x]=dep[x]-c[x],g[x]=INF;
    	son[x].resize(to[x].size()); // edit 1: fa
    	for(rg int i=0;i<(int)to[x].size();++i){
    		int y=to[x][i],w=we[x][i];
    		if(y==fa){
    			son[x][i]=INF;
    			continue;
    		}
    		dep[y]=dep[x]+w;
    		dfs1(y,x);
    		g[x]=min(g[x],f[y]);
    		son[x][i]=f[y];
    	}
    	f[x]=min(f[x],g[x]);
    	pre[x].resize(son[x].size()),suf[x].resize(son[x].size());
    	for(rg int i=0;i<(int)son[x].size();++i){
    		pre[x][i]=son[x][i];
    		if(i>0) pre[x][i]=min(pre[x][i],pre[x][i-1]);
    	}
    	for(rg int i=(int)son[x].size()-1;i>=0;--i){
    		suf[x][i]=son[x][i];
    		if(i<(int)son[x].size()-1) suf[x][i]=min(suf[x][i],suf[x][i+1]);
    	}
    }
    
    LL ans[N];
    void dfs2(int x,int fa,LL val){
    	ans[x]=dep[x]+c[x]+min(g[x]-2LL*dep[x],val);
    	for(rg int i=0;i<(int)to[x].size();++i){
    		int y=to[x][i];
    		if(y==fa) continue;
    		LL nval=dep[x]-c[x]-2LL*dep[x];
    		if(i>0) nval=min(nval,pre[x][i-1]-2LL*dep[x]);
    		if(i<(int)to[x].size()-1) nval=min(nval,suf[x][i+1]-2LL*dep[x]);
    		dfs2(y,x,min(val,nval));
    	}
    }
    int main(){
    	freopen("skylines.in","r",stdin),freopen("skylines.out","w",stdout);
    	read(n);
    	for(rg int i=1;i<=n;++i) read(c[i]);
    	for(rg int i=1;i<n;++i){
    		int u=read<int>(),v=read<int>();LL w=read<LL>();
    		to[u].push_back(v),we[u].push_back(w);
    		to[v].push_back(u),we[v].push_back(w);
    	}
    	dfs1(1,0);
    	dfs2(1,0,INF);
    	for(rg int q=read<int>();q--;) printf("%lld
    ",ans[read<int>()]);
    //	cerr<<clock()<<endl;
    	return 0;
    }
    

    其他做法

    刘老爷的点分治:还是那个式子,点分暴力做得了。时间复杂度(O(nlog n+q))

    黄学长的换根+线段树:线段树可以支持相邻两个点换根。时间复杂度(O(nlog n+q))

    空之轨迹(kiseki)

    【题目描述】

    Iri 近日沉迷游戏《英雄传说 VI 空之轨迹》。该游戏共有 n 个章节,第 i 章中有 ai 次战斗。当 第 i 章通关后,游戏会自动存档,然后自动进入第 i + 1 章,且不允许回到之前的章节(除非读档)。

    由于 Iri 的游戏设备年久失修,这天早上 Iri 进入游戏时,发现他的所有存档都消失了,只留下初始的一个序章存档(加载后会开始第 1 章)。不幸的是,游戏的章节切换系统也出现了 Bug,在每一章结束的自动存档之后,游戏会从已有的所有存档(包括序章存档)中等概率随机选取一个加载。由于 Iri 的耐心与精力有限,加载序章存档后,他只会连续玩 m 个章节,然后更换新的设备。 需要注意的是,游戏的存档系统没有损坏,即当第 i 章结束后的自动存档被加载后,一定会开始第 i + 1 章。

    现在 Iri 想知道,这 m 章内能进行的战斗总次数的期望值。Iri 觉得这个问题太简单了,所以就 把它交给了你。由于 Iri 的游戏技术同样出神入化,你可以认为所有章节他都会一次通关。

    【输入格式】

    从文件 kiseki.in 中读入数据。 第一行包含两个正整数 n, m. 第二行 n 个非负整数 ai .

    【输出格式】

    输出到文件 kiseki.out 中。

    输出一行一个整数,表示 Iri 进行的战斗总次数的期望值在模 998244353 意义下的值。 即设答案化为最简分式后的形式为 (frac{a}{b}) 其中 ab 互质。输出整数 x 使得 bxa (mod 998244353) 且 0 ≤ x < 998244353。可以证明这样的整数 x 是唯一的。

    【样例 1 输入】

    3 2
    1 2 1

    【样例 1 输出】

    499122179

    【样例 1 解释】

    答案是 (frac 52) 。由于 499122179 × 2 mod 998244353 = 5,所以你输出 499122179。

    【样例 2 输入】

    3 3
    5 5 1

    【样例 2 输出】

    332748132

    【样例 3 输入】

    15 10
    2 8 6 3 2 6 3 5 9 2 3 4 5 1 6

    【样例 3 输出】

    653958763

    【样例 4】

    见选手目录下的 kiseki/kiseki4.inkiseki/kiseki4.ans

    【子任务】

    测试点 n m 性质 1
    1 ≤ 10 ≤ 10
    2
    3 ≤ 105 ≤ 11
    4
    5
    6
    7 ≤ 21
    8
    9
    10
    11 ≤ 18
    12
    13
    14
    15 ≤ 21
    16
    17
    18
    19
    20

    对于具有性质 1 的数据,保证所有的 ai 都相等。

    对于所有数据,保证 1 ≤ m ≤ 21, mn ≤ 105 , 0 ≤ ai ≤ 10.

    【提示】

    xp−1 ≡ 1 (mod p),其中 p 是质数,x 是 [1, p) 上的整数。

    题解

    首先说一下题意了。如果玩到之前玩过的关卡,那么会对下一个关卡又建一个存档,也就是说选到下一个关卡的概率会增大。看一下简化版的题意:

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

    一个数列的权值和与数列中数的顺序无关,只与每种数的个数有关。

    考虑 DP,状态中需要记录每种数的个数。可以发现,将得到的数列排序后,相邻两数的差只能为 (0)(1)

    用二进制序列维护原数列的差分数组,即可表示数列中每种数的个数。

    (f(i, S)) 表示生成了 (i) 个数,已有数列的差分为 (S) 的方案数。

    (S)会如何变化呢?只能往(S)的末尾添(1),但是(0)可以随便加。所以(S)变化与它的加入位置有关。

    则每种数列出现的概率 (P(S) = frac {f(m,S)}{m!})

    事先预处理出 (tot_S) 表示状态 (S) 所表示的数列的权值和, 最后的答案即为(sum_S P(S) tot_S)

    复杂度:(O(m · 2^m))。注意做除法时用乘法逆元计算。

    说下状态的意思,状态的前两位是固定的01,表示序章存档和第一关存档,我们只需要记录后面的状态就行了。每次的转移的意思是跟简化版题意是一样。然后DP直接算概率,不是方案数。

    #include<bits/stdc++.h>
    #define co const
    #define il inline
    template<class T> T read(){
    	T x=0;char c=getchar();
    	while(!isdigit(c)) c=getchar();
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x;
    }
    template<class T> il T read(T&x){
    	return x=read<T>();
    }
    using namespace std;
    typedef long long LL;
    
    co int mod=998244353;
    il int add(int a,int b){
    	return (a+=b)>=mod?a-mod:a;
    }
    il int mul(int a,int b){
    	return (LL)a*b%mod;
    }
    
    co int N=30,S=1<<22;
    int n,a[N],inv[N];
    int f[S],g[S],c[N],p[N];
    
    int main(){
    	freopen("kiseki.in","r",stdin),freopen("kiseki.out","w",stdout);
    	read<int>(),read(n);
    	for(int i=1;i<=n;++i) read(a[i]);
    	inv[0]=inv[1]=1;
    	for(int i=2;i<=n;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
    	
    	f[0]=1,c[0]=1;
    	for(int i=2;i<=n;++i){
    		for(int s=0;s<1<<(i-2);++s){
    			int cur=1;
    			c[1]=1;
    			for(int j=0;j<i-2;++j){
    				if(s>>j&1) c[++cur]=1,p[cur]=j;
    				else ++c[cur];
    			}
    			for(int j=0;j<=cur;++j){
    				int t;
    				if(j==0) t=s<<1;
    				else if(j==cur) t=s|1<<(i-2);
    				else{
    					int h=s&((2<<p[j+1])-1);
    					t=(s^h)<<1|h;
    				}
    				g[t]=add(g[t],mul(c[j],mul(inv[i],f[s])));
    			}
    			f[s]=0;
    		}
    		swap(f,g);
    	}
    	
    	int ans=0;
    	for(int s=0;s<1<<(n-1);++s){
    		int cur=1,sum=a[1];
    		for(int j=0;j<n-1;++j){
    			if(s>>j&1) ++cur;
    			sum+=a[cur];
    		}
    		ans=add(ans,mul(sum,f[s]));
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    那个 n ​的范围还有这个模数肯定是骗人去想多项式。

  • 相关阅读:
    Python字典的初识、增删改查及嵌套
    Python列表的增删改查
    模块基础
    开放封闭原则和装饰器
    多层装饰器叠加装饰
    Python字符串的常用方法
    可迭代对象、迭代器对象和生成器对象
    日程安排组件dhtmlxScheduler汉化(转)
    dedecms在软件列表页调出下载链接
    [下载]《SAP R/3 IDES 4.71 中文版》
  • 原文地址:https://www.cnblogs.com/autoint/p/test20190816.html
Copyright © 2011-2022 走看看