NOIP2014 复盘

NOIP2014复盘

D1T1 P1328 生活大爆炸版剪刀石头布

他们按照周期出圈,按照表里写的去判断即可。签到题。

D1T2 P1351 联合权值

这道题的突破口在于距离为2是什么意思。我们先解决第二问。

在一颗有根树中,显然要不是爷孙关系,要不是兄弟关系。

爷孙关系好处理,遍历一下乘上去就完事。重点是兄弟关系如何处理。

有一个方便处理的结论:和的平方减去每个数分别的平方和,会等于两倍的所有数两两组合的乘积。

所以我们每到一层,就记录该层的和与平方和,通过计算就能获得兄弟的联合权值之和。

接下来解决第一问。

爷孙关系的最大值好找,每次都能独立地分析,直接取最大即可。但兄弟的最大值不好找。

可以发现,你只需要记录每一层中的最大值和次大值即可。直接分情况暴力维护就完事了。

绿题。

/*************************************************************************
 @Author: Garen
 @Created Time : Fri 22 Mar 2019 11:38:41 PM CST
 @File Name: P1351.cpp
 @Description:
 ************************************************************************/
#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
#define ll long long
const int maxn = 200005;
const ll MOD = 10007;
std::vector<int> G[maxn];
int n;
ll ans, maxv;
int fa[maxn], grandfa[maxn];
ll w[maxn];
void dfs(int u, int f) {
    fa[u] = f; grandfa[u] = fa[f];
    if(grandfa[u]) {
        ans += w[grandfa[u]] * w[u] * 2; ans %= MOD;
        maxv = std::max(maxv, w[grandfa[u]] * w[u]);
    }
    ll squaresum = 0, sum = 0;
    ll zuida = -1, cida = -2;
    for(auto v: G[u]) {
        if(v == f) continue;
        sum += w[v]; sum %= MOD;
        squaresum += w[v] * w[v]; squaresum %= MOD;
        if(w[v] > zuida) {
            cida = zuida; zuida = w[v];
        } else if(w[v] > cida) {
            cida = w[v];
        }
        dfs(v, u);
    }
    ans += sum * sum - squaresum; ans %= MOD;
    if(zuida > 0 && cida > 0) maxv = std::max(maxv, zuida * cida);
}
int main() {
    cin >> n;
    for(int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v); G[v].push_back(u);
    }
    for(int i = 1; i <= n; i++) cin >> w[i];
    dfs(1, 0);
    cout << maxv << ' ' << ans << endl;
    return 0;
}

D1T3 P1941 飞扬的小鸟

暴力去bfs还能拿挺多的分的。但要拿正解要dp。

下降可以无限下降,上升只能上升一次,这不就是01背包和完全背包吗?

我们记录\(dp[i][j]\)为到了第\(i\)条管子,现在高度为\(j\)的跳跃最少次数。

转移的时候挺有技巧的,这里提醒一下:

  1. 完全背包不需要枚举\(k\),不然会超时。正确做法是转移完全背包的时候从下往上去转移,像普通完全背包一样利用重复状态。
  2. 我们在转移的时候可以先视当前位置的管道不见,只要后面不转移到管道,我们这一层再设回0x3f3f3f3f就行了。
  3. 先转移完全背包,再转移01背包。
  4. 因为跳不出天花板,所以最高的一格需要去特判,直接从下面能转移的都转移来即可。

最后的答案就是最后一条管的所有dp的最小值,如果无解只要从后面往前找到第一个有解的就行了。

#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 10005, maxm = 1005;
const int INF = 0x3f3f3f3f;
int down[maxn], up[maxn];
bool b[maxn];
int fly[maxn], drop[maxn];
int dp[maxn][maxm];
int sum[maxn];
int n, m, p;
int main() {
    std::ios::sync_with_stdio(false);
    cin >> n >> m >> p;
    for(int i = 0; i < n; i++) cin >> fly[i] >> drop[i];
    for(int i = 1; i <= p; i++) {
        int x; cin >> x; cin >> down[x] >> up[x];
        b[x] = true;
    }
    for(int i = 0; i <= n; i++) {
        if(!b[i]) {
            down[i] = 0; up[i] = m + 1;
        }
    }
    sum[0] = b[0];
    for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + b[i];
    memset(dp, 0x3f, sizeof dp);
    for(int i = down[0] + 1; i < up[0]; i++) dp[0][i] = 0;
    for(int i = 1; i <= n; i++) {

        for(int j = fly[i - 1]; j <= m; j++) {
            if(j == m) {
                for(int k = m - fly[i - 1]; k <= m; k++) {
                    dp[i][j] = std::min(dp[i][j], dp[i - 1][k] + 1);
                    dp[i][j] = std::min(dp[i][j], dp[i][k] + 1);
                }
            }
            dp[i][j] = std::min(dp[i][j], dp[i - 1][j - fly[i - 1]] + 1);
            dp[i][j] = std::min(dp[i][j], dp[i][j - fly[i - 1]] + 1);
        }

        for(int j = down[i] + 1; j < up[i]; j++) {
            if(j + drop[i - 1] >= up[i - 1] || j + drop[i - 1] <= down[i - 1]) continue;
            dp[i][j] = std::min(dp[i][j], dp[i - 1][j + drop[i - 1]]);
        }

        for(int j = 1; j <= down[i]; j++) dp[i][j] = INF; 
        for(int j = up[i]; j <= m; j++) dp[i][j] = INF;
    }
    int ans = INF;
    for(int i = 0; i <= m; i++) ans = std::min(ans, dp[n][i]);
    if(ans == INF) {
        cout << 0 << endl;
        for(int i = n; i >= 1; i--) {
            for(int j = down[i] + 1; j < up[i]; j++) {
                if(dp[i][j] < INF) {
                    cout << sum[i] << endl;
                    return 0;
                }
            }
        }
    } else cout << 1 << endl << ans << endl;
    return 0;
}

D2T1 P2038 无限网络发射器选址

直接去暴力枚举正方形的位置即可,但是也要注意细节,注意不要数组越界了。

/*************************************************************************
 @Author: Garen
 @Created Time : Sat 09 Mar 2019 12:03:14 AM CST
 @File Name: P2038.cpp
 @Description:
 ************************************************************************/
#include<cstdio>
#include<algorithm>
#define ll long long
const ll maxn = 130;
ll a[maxn][maxn];
ll n, d;
ll cal(ll x, ll y, ll xx, ll yy) {
    return a[xx][yy] - a[x - 1][yy] - a[xx][y - 1] + a[x - 1][y - 1];
}
int main() {
    scanf("%lld %lld", &d, &n);
    for(ll i = 1; i <= n; i++) {
        ll x, y, k; scanf("%lld %lld %lld", &x, &y, &k);
        a[x + 1][y + 1] = k;
    }
    for(ll i = 1; i <= 129; i++) {
        for(ll j = 1; j <= 129; j++) {
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + a[i][j];
        }
    }
    ll ans = -1, cnt = 0;
    for(ll i = 1; i <= 129; i++) {
        for(ll j = 1; j <= 129; j++) {
            ll res = cal(std::max(1ll, i - d), std::max(1ll, j - d), std::min(129ll, i + d), std::min(129ll, j + d));
            if(res > ans) {
                ans = res; cnt = 1;
            } else if(res == ans) cnt++;
        }
    }
    printf("%lld %lld\n", cnt, ans);
    return 0;
}

D2T2 P2296 寻找道路

第一个条件说所有点的出边指向的点都直接或间接的与终点连通。

其实只要建立反向图,从终点开始遍历,所有搜到的点就都是满足此条件的。

接下来第二个条件就是sb了,直接从起点开始,在合法点上跑最短路即可。

D2T3 P2312 解方程

要直接写高精然后暴力算显然不现实,我们试着每步取膜的计算。

只要膜足够大并且是质数,可靠性还是超级高的。

显然一定要遍历从\(1\)\(m\)的每个\(x\)带进去计算。接下来就是如何判断是解。

高中数学必修3有学的话就知道有秦九韶公式,用递推的方式快速计算多项式的值。秦九韶nb

但是似乎秦九韶公式怎么写的还对答案有影响。。。这是玄学

有解就输出,其实知道了就没什么难度。

#include<cstdio>
#define ll long long
const ll maxn = 105, mod = 1000000007, maxm = 1e6 + 5;
ll n, m;
ll a[maxn];
ll b[maxm], cnt;
ll read()
{
    ll ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-') s = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans = (ans * 10 + ch - '0') % mod;
        ch = getchar();
    }
    return s * ans;
}
ll f(int x)
{
    int ans = 0;
    for(int i = n; i >= 1; i--) ans = ((a[i] + ans) * x) % mod;
    ans = (ans + a[0]) % mod;
    return ans;
}
int main()
{
    n = read(), m = read();
    for(int i = 0; i <= n; i++) a[i] = read();
    for(int i = 1; i <= m; i++)
    {
        if(f(i) == 0) b[cnt++] = i;
    }
    printf("%lld\n", cnt);
    for(int i = 0; i < cnt; i++) printf("%lld\n", b[i]);
    return 0;
}

转载于:https://www.cnblogs.com/Garen-Wang/p/11619076.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值