[题解]NOIP2017 Day1 Solution - by xyz32768

我冒了严寒,去到相隔二万余里,二十多年不见的NOIP赛场去。——出自《或许我就是蒟蒻吧》

T1 math

作为Day1T1,这一题不是一般的坑……
首先设一个合法选择为 x ,则必须满足这两个条件:
1、ax
2、对于所有的 0i 并且 xia0 ,有 b(xia)
可以发现,在第二个条件中,满足 0i 并且 xia0 i xa+1个。
而由于 a,b 互质,所以 x 不断减去a后对 b 的模数是满足周期性变化的,并且周期长度为b。所以得到,凡是 xa+1b 的数都是不合法的。
考虑在 xa=b1 的范围内找答案。这种情况下如果不断地把 x 减去a,算上 x 本身一共会得到b1种结果。而这 b1 种结果已经遍历了对 b b1种模数,还有一个模数没有遍历到,而这个模数就是 0 。而这时候恰好就是让x减掉 aba 后是 b 的倍数。但显然x是不能取 aba 的,所以 x 的最大值为abab这里写图片描述
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a, b;
int main() {
    //freopen("math.in", "r", stdin);
    //freopen("math.out", "w", stdout);
    cin >> a >> b;
    cout << 1ll * a * b - a - b << endl;
    return 0;
}

T2 complexity

大模拟……利用一点点栈的思想……注意细节。
1、x n, O(n)
2、n n, O(1)
3、x y, x<=y O(1) ,否则 O(0) (无法执行)。
4、n x, O(0)
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 504, INF = 0x3f3f3f3f;
char s[N][N], com[N]; int L, top, stk[N], st[N], ed[N];
char var[N];
void Get(int id, int to) {
    int i, n = strlen(s[id] + 1); i = 2; st[to] = ed[to] = 0;
    while (s[id][i] == ' ') i++; var[to] = s[id][i++];
    while (s[id][i] == ' ') i++;
    while (s[id][i] != ' ') {
        if (s[id][i] == 'n') st[to] = INF;
        else st[to] = st[to] * 10 + (s[id][i] - '0');
        i++;
    }
    while (s[id][i] == ' ') i++;
    while (s[id][i] != ' ' && s[id][i] != 0) {
        if (s[id][i] == 'n') ed[to] = INF;
        else ed[to] = ed[to] * 10 + (s[id][i] - '0');
        i++;
    }
}
void work() {
    int i, j, CM = 0, nowCm = 0, stCM = 0;
    L = read(); scanf("%s\n", com + 1); top = 0;
    for (i = 1; i <= L; i++) gets(s[i] + 1);
    for (i = 1; i <= L; i++) {
        if (s[i][1] == 'F') {
            stk[++top] = i; Get(i, top); nowCm = 0;
            for (j = 1; j < top; j++) {
                if (var[j] == var[top])
                    return (void) puts("ERR");
            }
            for (j = 1; j <= top; j++)
                if (st[j] != INF && ed[j] == INF) nowCm++;
                else if (st[j] == INF && ed[j] != INF) break;
                else if (st[j] != INF && ed[j] != INF) {
                    if (st[j] > ed[j]) break;
                }
            CM = max(CM, nowCm);
        }
        else {
            top--;
            if (top < 0) return (void) puts("ERR");
        }
    }
    if (top > 0) return (void) puts("ERR");
    if (com[3] == '1') stCM = 0;
    else {
        int w = strlen(com + 1);
        for (i = 5; i < w; i++) stCM = stCM * 10 + (com[i] - '0');
    }
    puts(CM == stCM ? "Yes" : "No");
}
int main() {
    //freopen("complexity.in", "r", stdin);
    //freopen("complexity.out", "w", stdout);
    int T = read();
    while (T--) work();
    return 0;
}

T3 park

稍有难度的题来了……在考场上Tarjan写炸丢了30分[挫败]
先在反图上以 n 为起点跑最短路,记dis[i] i n的最短路。
先考虑有无数条合法路径的条件,容易想到,有无数条合法路径,当且仅当图存在零环并且存在一条长度不大于 dis[1]+K 1 n的路径经过了在零环上的点。判断一个点是否在零环上,可以用Tarjan强连通分量等技巧实现。判断是否在 1 n的合法路径上,即判断是否满足:
1 到该节点的最短距离+该节点到n的最短距离 dis[1]+K
然后DP,看到 K 很小,就可以想到模型:
f[i,j]表示 i n,距离为 dis[i]+j 的方案数。
转移就是( k 为与i相邻(存在边 <i,k <script type="math/tex" id="MathJax-Element-57"> val<i,k> 为边 <i,k> <script type="math/tex" id="MathJax-Element-59"> </script>的边权):
f[i,j]=kf[k,dis[i]+jval<i,k>dis[k]]
刚开始发现这个方程有点儿问题(转移时出现环),但后来发现,
由于 val<i,k>+dis[k] 一定不小于 dis[i] ,所以 dis[i]+jval<i,k>dis[k] 一定不大于 j ,因此不存在零环的情况下,转移是不会出现环的。
由于j相同的情况下,只能从 dis 较小的点转移到 dis 较大的点,所以可以将每个点按照 dis 为关键字排序(蒟蒻用了记忆化搜索)进行DP。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5, M = 2e5 + 5, E = 53, INF = 0x3f3f3f3f;
int n, m, ecnt, K, nxt[M], adj[N], go[M], val[M], f[N][E], dis[N],
nxt2[M], adj2[N], go2[M], val2[M], ecnt2, que[M << 3], len, PYZ,
times, sum, bel[N], stk[N], num[N], top, dfn[N], low[N], disS[N];
bool vis[N], VIS[N], ins[N];
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
    nxt2[++ecnt2] = adj2[v]; adj2[v] = ecnt2; go2[ecnt2] = u; val2[ecnt2] = w;
}
void Tarjan(int u) {
    dfn[u] = low[u] = ++times; ins[stk[++top] = u] = 1;
    for (int e = adj[u], v; e; e = nxt[e]) {
        if (val[e]) continue;
        if (!dfn[v = go[e]]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (ins[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        num[bel[u] = ++sum] = 1; ins[u] = 0; int v;
        while (v = stk[top--], v != u) num[bel[v] = sum]++, ins[v] = 0;
    }
}
void spfa(int S) {
    int i; memset(dis, INF, sizeof(dis));
    dis[que[len = 1] = S] = 0;
    for (i = 1; i <= len; i++) {
        int u = que[i]; vis[u] = 0;
        for (int e = adj[u], v; e; e = nxt[e])
            if (dis[u] + val[e] < dis[v = go[e]]) {
                dis[v] = dis[u] + val[e];
                if (!vis[v]) vis[que[++len] = v] = 1;
            }
    }
    for (i = 1; i <= n; i++) disS[i] = dis[i];
}
void SPFA(int S) {
    int i; memset(dis, INF, sizeof(dis));
    dis[que[len = 1] = S] = 0;
    for (i = 1; i <= len; i++) {
        int u = que[i]; vis[u] = 0;
        for (int e = adj2[u], v; e; e = nxt2[e])
            if (dis[u] + val2[e] < dis[v = go2[e]]) {
                dis[v] = dis[u] + val2[e];
                if (!vis[v]) vis[que[++len] = v] = 1;
            }
    }
}
bool zeroCycle() {
    times = sum = 0; int i; memset(num, 0, sizeof(num));
    memset(dfn, 0, sizeof(dfn));
    for (i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i);
    for (i = 1; i <= n; i++) if (num[bel[i]] > 1
        && disS[i] + dis[i] <= dis[1] + K) return 1;
    return 0;
}
int DP(int u, int x) {
    if (f[u][x] != -1) return f[u][x];
    if (!x && u == n) return f[u][x] = 1; f[u][x] = 0;
    for (int e = adj[u], v; e; e = nxt[e]) {
        int d = dis[u] + x - val[e] - dis[v = go[e]];
        if (d >= 0 && d <= K)
            (f[u][x] += DP(v, d)) %= PYZ;
    }
    return f[u][x];
}
void work() {
    memset(bel, 0, sizeof(bel));
    memset(vis, 0, sizeof(vis));
    memset(VIS, 0, sizeof(VIS));
    ecnt = ecnt2 = 0; memset(adj, 0, sizeof(adj));
    memset(adj2, 0, sizeof(adj2));
    memset(f, -1, sizeof(f));
    int i, x, y, z; n = read(); m = read(); K = read(); PYZ = read();
    for (i = 1; i <= m; i++) {
        x = read(); y = read(); z = read();
        add_edge(x, y, z);
    }
    spfa(1); SPFA(n); int ans = 0; if (zeroCycle()) return (void) puts("-1");
    for (i = 0; i <= K; i++) (ans += DP(1, i)) %= PYZ;
    printf("%d\n", ans);
}
int main() {
    //freopen("park.in", "r", stdin);
    //freopen("park.out", "w", stdout);
    int T = read();
    while (T--) work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值