NOI2014

  没怎么想正解,打了一个70分暴力

  考虑到OR与OR之间的运算顺序对答案没有影响,即 ( ans OR x ) OR y 与 ( ans OR y ) OR x 相等,可以将多个连续的OR运算合并成一个,AND,XOR 同理

  暴力枚举初始攻击力 0 ~ m

  又存在多个数 OR/XOR/AND x 答案相等,如 5 OR 10 = 15,   6 OR 10 = 15,   13 OR 10 = 15,可记忆化

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int n, m, num;
set<pair<int, int> >st;

struct node
{
    char op; int x;
    node(){}
    node(char o, int xx) {op = o; x = xx;}
}q[N];

int main()
{
    n = read(); m = read();
    for(int i = 1; i <= n; i++)
    {
        char s[5]; scanf("%s", s);
        if(s[0] != q[num].op) q[++num] = node(s[0], read());
        else
        {
            int x = read();
            if(q[num].op == 'O') q[num].x |= x;
            else if(q[num].op == 'X') q[num].x ^= x;
            else q[num].x &= x;
        }
        if(q[num].op == 'A' && !q[num].op)
        {
            num = 1;
            q[num] = node('A', 0);
        }
    }
    int ans = 0;
    for(int x = 0; x <= m; x++)
    {
        int t = x; bool fail = false;
        for(int i = 1; i <= num; i++)
        {
            if(q[i].op == 'O') t |= q[i].x;
            else if(q[i].op == 'X') t ^= q[i].x;
            else t &= q[i].x;
            if(st.find(make_pair(i, t)) == st.end()) st.insert(make_pair(i, t));
            else {fail = true; break;}
        }
        if(!fail) ans = max(ans, t);
    }
    cout << ans;
    return 0;
}
70分代码

  

  emmm......

  然后又想了一下正解,也很简单啊,早知道就不去耗第三题了

  单独考虑二进制下的每一位,分别计算初始值选择 0 / 1 得到的最终答案

  初始值有上限,所以当选 0 / 1 效果相同时选 0,仅当选 1 效果更优且不会使值大于 m 时选 1

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int n, m, num;
set<pair<int, int> >st;

struct node
{
    char op; int x;
    node(){}
    node(char o, int xx) {op = o; x = xx;}
}q[N];

int deal(int k, int i, int x)
{
    if(k != -1)
    {
        int t = (q[i].x & (1 << k))? 1:0;
        if(q[i].op == 'O') return x | t;
        if(q[i].op == 'X') return x ^ t;
        if(q[i].op == 'A') return x & t;
    }
    else
    {
        if(q[i].op == 'O') return x | q[i].x;
        if(q[i].op == 'X') return x ^ q[i].x;
        if(q[i].op == 'A') return x & q[i].x;
    }
}

int main()
{
    n = read(); m = read();
    for(int i = 1; i <= n; i++)
    {
        char s[5]; scanf("%s", s);
        if(s[0] != q[num].op) q[++num] = node(s[0], read());
        else
        {
            int x = read();
            if(q[num].op == 'O') q[num].x |= x;
            else if(q[num].op == 'X') q[num].x ^= x;
            else q[num].x &= x;
        }
        if(q[num].op == 'A' && !q[num].op)
        {
            num = 1;
            q[num] = node('A', 0);
        }
    }
    int ans = 0;
    for(int k = 30; k >= 0; k--)
    {
        if((1 << k) > m) continue;
        int x[2] = {0, 1};
        for(int i = 1; i <= num; i++)
        {
            x[0] = deal(k, i, x[0]);
            x[1] = deal(k, i, x[1]);
        }
        if(x[1] && !x[0])
            if(ans + (1 << k) <= m) ans += (1 << k);
    }
    for(int i = 1; i <= num; i++) ans = deal(-1, i, ans);
    cout << ans;
    return 0;
}
AC代码

 

 

  数据有点水啊,二分 + dfs有20分,竟然过掉了一个500,3000的点

#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
const int M = 200005;

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int n, m, ax, bx;

int num, tmp1[M], to[M], tmp2[M], va[M], tmp3[M], vb[M], tmp4[M], nt[M], tmp5[M], p[N];
void add(int x, int y, int a, int b) {to[++num] = y; va[num] = a; vb[num] = b; nt[num] = p[x]; p[x] = num;}

bool tmp[M], vis[N];
bool dfs(int x, int a, int b, int mx)
{
    if(a + b > mx) return false;
    if(x == n) return true;
    bool f = false;
    for(int e = p[x]; e; e = nt[e])
    {
        if(vis[to[e]]) continue;
        vis[to[e]] = true;
        if(dfs(to[e], max(a, va[e]), max(b, vb[e]), mx)) return true;
        vis[to[e]] = false;
    }
    return false;
}

int main()
{
    n = read(); m = read();
    for(int i = 1; i <= m; i++)
    {
        int x = read(), y = read(), a = read(), b = read();
        if(x == y) continue;
        add(x, y, a, b); add(y, x, a, b);
        ax = max(ax, a); bx = max(bx, b);
    }
    int l = 0, r = ax + bx, ans = -1;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        memset(vis, false, sizeof(vis)); vis[1] = true;
        if(dfs(1, 0, 0, mid)) {ans = mid; r = mid - 1;}
        else l = mid + 1;
    }
    cout << ans;
    return 0;
}
20分代码

  

  若只存在一个权值,即求最大边权最小,直接 spfa 即可

  对于两种权值限制,可先对a进行排序,依次加边并对 b 跑 spfa,ans = max( ai + dis[n] );

  每次添加的边仅对两端点有影响,不必重跑 spfa

#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
const int M = 200005;

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

int n, m;

struct node
{
    int x, y, a, b;
    node(){}
    node(int xx, int yy, int aa, int bb) {x = xx; y = yy; a = aa; b = bb;}
}q[M];

bool cmp(node A, node B) {return A.a < B.a;}

int num, to[M], w[M], nt[M], p[N];
void add(int x, int y, int v) {to[++num] = y; w[num] = v; nt[num] = p[x]; p[x] = num;}

int dis[N]; bool vis[N]; queue<int> Q;
void spfa()
{
    while(!Q.empty())
    {
        int k = Q.front(); Q.pop(); vis[k] = false;
        for(int e = p[k]; e; e = nt[e])
        {
            if(dis[to[e]] > max(dis[k], w[e]))
            {
                dis[to[e]] = max(dis[k], w[e]);
                if(!vis[to[e]]) {vis[to[e]] = true; Q.push(to[e]);}
            }
        }
    }
}

int main()
{
    n = read(); m = read();
    for(int i = 1; i <= m; i++)
    {
        q[i].x = read(); q[i].y = read(); q[i].a = read(); q[i].b = read();
    }
    sort(q + 1, q + 1 + m, cmp);
    memset(dis, 63, sizeof(dis)); dis[1] = 0;
    int ans = 1e8;
    for(int i = 1; i <= m; i++)
    {
        add(q[i].x, q[i].y, q[i].b); add(q[i].y, q[i].x, q[i].b);
        Q.push(q[i].x); Q.push(q[i].y); vis[q[i].x] = vis[q[i].y] = true;
        spfa();
        ans = min(ans, dis[n] + q[i].a);
    }
    if(ans == 1e8) cout << -1; else cout << ans;
    return 0;
}
spfa

  显然复杂度太玄学过不了

 

  哎呀,好尴尬

  显然复杂度太玄学过不掉

  再次考虑先前的操作,对 b 求经过的最小的最大边权,可用最小生成树求解

   按 a 从小到大依次加边,即维护动态最小生成树,lct

还没写完。。。
AC代码

 

  太麻烦啦,不想写

 

day1小结

  第一次提交:70 + 10 + 0

  不看题解进行修改:100 + 20 + 0

  emmmmm,NOI要真考成这要得出大事。。。

转载于:https://www.cnblogs.com/XYZinc/p/9041332.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
P2375 [NOI2014] 动物园是一道经典的动态规划题目,以下是该题的详细题意和解题思路。 【题意描述】 有两个长度为 $n$ 的整数序列 $a$ 和 $b$,你需要从这两个序列中各选出一些数,使得这些数构成一个新的序列 $c$。其中,$c$ 序列中的元素必须在原序列中严格递增。每个元素都有一个价值,你的任务是选出的元素的总价值最大。 【解题思路】 这是一道经典的动态规划题目,可以采用记忆化搜索的方法解决,也可以采用递推的方法解决。 记忆化搜索的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int dfs(int x, int y) { if (dp[x][y] != -1) return dp[x][y]; if (x == n || y == n) return 0; int res = max(dfs(x + 1, y), dfs(x + 1, y + 1)); if (a[x] > b[y]) { res = max(res, dfs(x, y + 1) + b[y]); } return dp[x][y] = res; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); memset(dp, -1, sizeof(dp)); printf("%d\n", dfs(0, 0)); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值,-1表示未计算过。dfs(x,y)表示选到a数组中第x个元素和b数组中第y个元素时的最大价值,如果dp[x][y]已经计算过,则直接返回dp[x][y]的值。如果x==n或者y==n,表示已经遍历完一个数组,直接返回0。然后就是状态转移方程了,如果a[x] > b[y],则可以尝试选b[y],递归调用dfs(x, y+1)计算以后的最大价值。否则,只能继续遍历数组a,递归调用dfs(x+1, y)计算最大价值。最后,返回dp[0][0]的值即可。 递推的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); for (int i = n - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]); if (a[i] > b[j]) { dp[i][j] = max(dp[i][j], dp[i][j + 1] + b[j]); } } } printf("%d\n", dp[0][0]); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值。从后往前遍历数组a和数组b,依次计算dp[i][j]的值。状态转移方程和记忆化搜索的方法是一样的。 【参考链接】 P2375 [NOI2014] 动物园:https://www.luogu.com.cn/problem/P2375

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值