20190915模拟赛

T1.phantasm

题意

题解

先用总的长度(n1)(n-1)减去每次至少跳kk的限制,即令N=(n1)(m1)kN=(n-1)-(m-1)*k,然后用插板法,答案就是C(n+m2,m2)C(n+m-2,m-2)

要求的是奇偶性。也就是模22,考场上我先写了LucasLucas,时间复杂度O(TlogN)O(TlogN),初步判定能过(毕竟良心出题人时限2.5s2.5s)。

然后发现可直接统计组合数分子分母分别有多少22次幂作比较,只有当上下22次幂相等才是奇数。

我认为这样常数更小,就成了这个,但还是O(TlogN)O(TlogN)。不过要做33loglog,分别是n,m,nmn,m,n-m,就还有个33的常数。实测比LucasLucas慢。

看了题解,发现可以根据卢卡斯定理得出,C(n,k)C(n,k)为奇数当且仅当n,kn,k在二进制下的每一位nn都要大于等于kk,否则就会在某一个过程得到C(0,1)=0C(0,1)=0,然后就为偶数了。所以当答案为奇数,当且仅当n&k=kn&k=k。所以就O(n)O(n)了。良心出题人。。

CODE

O(n)O(n)极简代码

#include<cstdio>
int main(){
	int n,m,k,T;scanf("%d",&T);
	while(T--)scanf("%d%d%d",&n,&m,&k), puts(((n-(m-1)*k+m-3)&(m-2))==m-2?"Yes":"No");
}

T2.skylines

题意

题解

可以发现对于一个点uu,要求的就是minv c[u]c[v]+dis[u]+dis[v]2dis[lca(u,v)]=c[u]+dis[u]+minv dis[v]c[v]2dis[lca(u,v)]min_v c[u]-c[v]+dis[u]+dis[v]-2*dis[lca(u,v)]\=c[u]+dis[u]+min_v dis[v]-c[v]-2*dis[lca(u,v)]

所以我们在lcalca处考虑贡献,vv能对uu贡献,它们一定在lcalca的不同子树内。所以我们dfsdfs遍历lcalca的所有子树,存下来(dis[i]c[i])(dis[i]-c[i])的最小值和次小值,同时保证这两个值不在同一子树内。那么就可以对一棵子树下面所有的点来进行贡献。如果最小值出在这棵子树,就用次小值更新;否则就用最小值更新。

然后我想到子树求minmin就写了dfsdfs序+线段树,然后最后下放标记。O(nlogn)O(nlogn)(思维僵化)。还是过了,良心出题人。

发现可以直接打到点上,最后下放标记。O(n)O(n)

CODE

两次dfsdfs,类似树形DPDP

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline void read(int &x) {
    char ch; while(!isdigit(ch=getchar()));
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
}
const int MAXN = 200005;
const LL INF = 1ll<<50;
int n, m, c[MAXN], fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], w[MAXN<<1], cnt;
inline void link(int u, int v, int wt) {
    to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; w[cnt] = wt;
    to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt; w[cnt] = wt;
}
LL dis[MAXN], mn[MAXN], lz[MAXN], ans[MAXN];
void dfs(int u, int ff) {
    mn[u] = dis[u] - c[u];
    LL val[2] = { mn[u], 1ll<<50 }, bel[2] = { u, -1 };
    for(int i = fir[u], v; i; i = nxt[i])
        if((v=to[i]) != ff) {
			dis[v] = dis[u] + w[i], dfs(v, u);
            mn[u] = min(mn[u], mn[v]);
            if(mn[v] < val[0]) val[1] = val[0], bel[1] = bel[0], val[0] = mn[v], bel[0] = v;
            else if(mn[v] < val[1]) val[1] = mn[v], bel[1] = v;
        }
    for(int i = fir[u], v; i; i = nxt[i])
        if((v=to[i]) != ff) {
            if(bel[0] != v) lz[v] = min(lz[v], val[0]-2*dis[u]);
            else if(bel[1] != v && bel[1] != -1) lz[v] = min(lz[v], val[1]-2*dis[u]);
        }
    if(bel[0] != u) ans[u] = min(ans[u], val[0]-2*dis[u]);
    else if(bel[1] != u && bel[1] != -1) ans[u] = min(ans[u], val[1]-2*dis[u]);
}
inline void getans(int u, int ff) {
	lz[u] = min(lz[u], lz[ff]);
	ans[u] = min(ans[u], lz[u]);
	for(int i = fir[u], v; i; i = nxt[i])
        if((v=to[i]) != ff)
            getans(v, u);
}
int main () {
    read(n);
    for(int i = 1; i <= n; ++i) read(c[i]), lz[i] = ans[i] = INF;
    for(int i = 1, x, y, z; i < n; ++i)
        read(x), read(y), read(z), link(x, y, z);
    dfs(1, 0), getans(1, 0);
	int q, x; read(q);
    while(q--) read(x), printf("%lld
", ans[x] + dis[x] + c[x]);
}

T3.kiseki

题意

同略

题解

考场上傻逼了。感觉整道题很麻烦,多个存档很难搞。然后就不会了。

发现一定只有前mmaa值有用。所以nn1052110^5 o21

所以正解就是O(m2m)O(m2^m)的算法。所以其实有O(m2)O(m^2)的算法。

概率DPDP,设f[i][j]f[i][j]表示第ii个轮玩第jj章的概率。然后发现转移十分显然:f[i][j]=k=0i1f[k][j1]f[i][j]=sum_{k=0}^{i-1}f[k][j-1]

最后答案就是f[i][j]a[j]sum f[i][j]*a[j]

这样是O(m3)O(m^3)(虽然对于这道题已经十分优秀了)

发现转移可以前缀和优化。时间达到了O(m2)O(m^2)良心出题人(?)不卡O(m^3)
如果前缀和数组只开一维的话要注意转移时jj从大到小枚举。

所以说标算被碾了,mm可以开到5k5kO(m2m)O(m2^m)标算见文末。

CODE

#include <cstdio>
const int MAXM = 22;
const int mod = 998244353;
int n, m, inv[MAXM], f[MAXM][MAXM], a[MAXM], sum[MAXM];
int main () {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) scanf("%d", &a[i]);
	inv[0] = inv[1] = 1;
	for(int i = 2; i <= m; ++i) inv[i] = 1ll * (mod - mod/i) * inv[mod%i] % mod;
	f[0][0] = sum[0] = 1;
	int ans = 0;
	for(int i = 1; i <= m; ++i)
		for(int j = i; j >= 1; --j) {
			f[i][j] = 1ll * sum[j-1] * inv[i] % mod;
			sum[j] = (sum[j] + f[i][j]) % mod;
			ans = (ans + 1ll * f[i][j] * a[j] % mod) % mod;
		}
	printf("%d
", ans);
}

标算就是把当前所有存档按标号排序得到一个长度最大为mm的数组,差分后元素只会有0 or 10 or 1,就可以状压了。f(i,S)f(i, S)表示第ii轮,已有存档数列的差分为SS的方案数。则每种数列出现的概率PS=f(m,S)m!P_S=frac{f(m,S)}{m!}转移时枚举前一个数的大小,修改状态即可事先预处理出totStot_S表示状态SS所表示的数列的权值和,最后的答案即为SPStotSsum_SP_Stot_S复杂度:O(m2m)O(m2^m)

代码略。

原文地址:https://www.cnblogs.com/Orz-IE/p/12039223.html