T1.phantasm
题意
略
题解
先用总的长度减去每次至少跳的限制,即令,然后用插板法,答案就是
要求的是奇偶性。也就是模,考场上我先写了,时间复杂度,初步判定能过(毕竟良心出题人时限)。
然后发现可直接统计组合数分子分母分别有多少次幂作比较,只有当上下次幂相等才是奇数。
我认为这样常数更小,就成了这个,但还是。不过要做次,分别是,就还有个的常数。实测比慢。
看了题解,发现可以根据卢卡斯定理得出,为奇数当且仅当在二进制下的每一位都要大于等于,否则就会在某一个过程得到,然后就为偶数了。所以当答案为奇数,当且仅当。所以就了。良心出题人。。
CODE
极简代码
#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
题意
略
题解
可以发现对于一个点,要求的就是
所以我们在处考虑贡献,能对贡献,它们一定在的不同子树内。所以我们遍历的所有子树,存下来的最小值和次小值,同时保证这两个值不在同一子树内。那么就可以对一棵子树下面所有的点来进行贡献。如果最小值出在这棵子树,就用次小值更新;否则就用最小值更新。
然后我想到子树求就写了序+线段树,然后最后下放标记。(思维僵化)。还是过了,良心出题人。
发现可以直接打到点上,最后下放标记。
CODE
两次,类似树形。
#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
题意
同略
题解
考场上傻逼了。感觉整道题很麻烦,多个存档很难搞。然后就不会了。
发现一定只有前个值有用。所以从。
所以正解就是的算法。所以其实有的算法。
概率,设表示第个轮玩第章的概率。然后发现转移十分显然:
最后答案就是。
这样是(虽然对于这道题已经十分优秀了)
发现转移可以前缀和优化。时间达到了。良心出题人(?)不卡O(m^3)
如果前缀和数组只开一维的话要注意转移时从大到小枚举。
所以说标算被碾了,可以开到。标算见文末。
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);
}
标算就是把当前所有存档按标号排序得到一个长度最大为的数组,差分后元素只会有,就可以状压了。表示第轮,已有存档数列的差分为的方案数。则每种数列出现的概率转移时枚举前一个数的大小,修改状态即可事先预处理出表示状态所表示的数列的权值和,最后的答案即为复杂度: 。
代码略。