10-3 DAIRY

这几天也没有什么考场失分原因,全是打暴力,能打则打不打就挂这样。
T1 string
//很不幸,暴力都调了很久。

p.s.(可能只有本人才看得懂的->)sort里面是从a[i].l 到a[i].r+1 就是要注意这个+1…我试过了在各个地方+1/-1什么的才知道原来调用sort还有这些要注意的。

【solution写的是】
用线段树维护区间内 a~z 的个数,每次询问拆成 26 个区间修改操作。

还没理解透。所以也没写,而且看了标程和同学们的程序我觉得…真的不想写,太复杂太多细节了(线段树

T2 matrix
[10/10/2017 10:45am - 完了完了。 这个做法只有70分!!!改好后会传100分的!!!]
(可忽略,尊重出题人放在这里给与参考):

【solution写的是】
用 f[i][j]表示做到前 i 列,已经有 j 列在右侧区间放 1 的方案数。i
越过一个左侧区间的右端点时,从之前剩下的空列中选一列在这个左侧区间放 1。转移时分在右侧区间放 1或不放 1。

【看懂了请自动忽略我写的题解,乱七八糟的,我也没完全弄清楚,很懵b】

我应该不是世界上最后一个看不懂solution的人(吧),所以see一see我问的别人的思路。如果有任何错误的地方欢迎指出!!!【因为我也写的很虚】
(21:56pm : 好的写完题解我看懂solution了)

输入左端点a、右端点b。我们将l[a]++,r[b]++.
l[i]表示以第i列为左端点的有几行,r[i]表示以第i列为右端点的有几行。
题目要求每一行放两个1, 一个在左端点及左,一个在右端点及右。并且要求每一列最多只存在一个1。

和solution稍有不同,我们定义dp[i][j]表示当前考虑到第i列,右区间还剩j列没放1时的方案数。sum[i]记录r[i]前缀和。
从题目性质出发考虑,左区间和右区间互不影响,所以在这里是一个乘法原理,答案是左区间方案数*右区间方案数。

  • 我们先来考虑右区间。这里我们分成(放1or不放1)两部分计算。
  • 不放1:直接从上一列推过来。dp[i][j] = dp[i-1][j-r[i]] (r[i] <= j <= sum[i])
  • 放1:dp[i-1][j] += dp[i][j] * j (1 <= j <= sum[i]) //放1和不放1符合的是加法原理
  • 左区间。我们用res记录所有列-左区间的列数(也就是中间不用放1部分+右区间)。
  • 此时dp[i][j] = dp[i][j] * (res-(sum[i]-j)) (0 <= j <= sum[i]) 做1~l[i]次。
  • res我不知道怎么解释,口胡一下吧:你可以再开一个sumL[]记录l[]的前缀和,res = i-sumL[i]; 因为我们是遇到一个左端点就在之前的区间的空列里放1,所以就是这样。【真的口胡】

基本ok.
初始:dp[0][0] = 1;
结束:求dp[m][0];

改正确的程序如下:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;
const int N = 3010;
const int p = 998244353;

int n, m, res;
int l[N], r[N], sum[N];
ll dp[N][N];

template <typename T>
T read(){
    T N(0), F(1);
    char C = getchar();
    for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
    for(; isdigit(C); C = getchar()) N = N*10 + C-48;
    return N*F;
}

int main(){
    freopen("b.in", "r", stdin);
    freopen("b.out","w",stdout);

    n = read<int>(); m = read<int>();
    for(int i = 1; i <= n; i++){
        int a, b;
        a = read<int>(); b = read<int>();
        l[a]++; r[b]++;
    }

    for(int i = 1; i <= m; i++) sum[i] = sum[i-1] + r[i];
    dp[0][0] = 1;
    for(int i = 1; i <= m; i++){
        res++;
        //右
        for(int j = r[i]; j <= sum[i]; j++) dp[i][j] = dp[i-1][j-r[i]];//不放1
        for(int j = 1; j <= sum[i]; j++) dp[i][j-1] += (dp[i][j] * (ll)j % (ll)p)%p;//放1
        for(int k = 1; k <= l[i]; k++){
            for(int j = 0; j <= sum[i]; j++) dp[i][j] = dp[i][j] * (ll)(max(0, res-sum[i]+j))%(ll)p;//左
            res--;
        }
    }
    printf("%lld\n", dp[m][0]);
    return 0;
}

T3 big

【solution写的是】
对手做的是将 x 在二进制下左移一位。假设在异或 i 个数后左移,等价于开始时先左移,然后把前 i 个数左移一位。
问题转化为选一个数,使它左移一位后,与 m+1 个给定数分别异或的最小值最大。 将 m+1
个数建立一棵字典树,从上到下遍历来最大化结果:走到一个点时,如果往下只有 0,说明我们这一位取 1异或后只能是
1,累计结果中加上这一位的值,只有 1 也一样;如果既有 0 又有 1,说明这一位无论怎么取最后都是 0,分别往下走即可。

对我来说根本不可做啊啊啊!字典树都不会写。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值