NEERC2014训练日志

solved 4 (A B J K)

rank 144/237

卡题,卡题,卡题,每题都卡!!!好几个题,都是因为小细节上写错,导致卡了很久的。

多训练,多打比赛,总结新题和陈题之间的异同,把一些不良的编码习惯给磨掉,竞赛水平可提升很多。

https://vjudge.net/contest/220476

A - Alter Board (通过)

<qj>

一开始选择了错误的贪心思想,后面尝试画出3*3矩阵的时候找到了正确思路:

由于一开始的矩阵是全都错开的:

# @ #

@#@

# @ #

我们把第二列翻转可得到

#  #  #

@@@

#  #  #

结果就很明显了。(以后一道题找不到代码bug就要考虑思路bug,换人重新读题做一遍比较好。)

B-Burrito King (通过)

<qj>

题意:在满足A的开心度的同时,要照顾B的不开心度,贪心求A的最大开心度。

思路:

由于可取浮点数,那么每类物品的A开心度和B不开心度的比值就是我们贪心的方向。

按比例由大到小排序便可,这样保证最优。

求解时,推一个式子便可。

注意数据范围!!!有很多个数据都包括了0

F-Filter

 一、题意

  给一个数字$m$,表示后面说的数据文件包含的记录的二进制位数;一个$f$,表示后面有$f$个哈希函数,接下来$f$个数字$a_i(0 \le i < f)$,表示哈希函数的参数;接下来给一个数字$n$,表示后面有$n$个数据文件;接下来给$n$行字符串,代表数据文件中记录的十六进制($0123456789abcdef$)表示(注意:这是题目的关键点。数据文件的十六进制表示,对应的二进制,是按字符大端存储的。即:比如第一个字符是$e$,二进制表示为$1110$,那么,该数据文件中记录的第$0-3$位分别是:$0$、$1$、$1$、$1$。按字符大端存储的意思就是说:一个字符二进制表示的高位(在这儿右边的是高位,因为是字符串嘛),对应实际数据文件记录的低位。可能没讲清楚。通俗化表示就是:拿样例说话,$m=23$,第一个数据文件的记录为$effde7$,对应的二进制表示为$1110\ 1111\ 1111\ 1101\ 1110\ 0111$,那么,数据记录的第$0-22$位分别为:0-1-1-1 1-1-1-1 1-1-1-1 1-0-1-1 0-1-1-1 1-1-1。另外,题目中强调了最后一个字符的存储模式。讲的很绕,硬是猜出来的意思。然而,比赛过程中,就是被这个地方给坑了。其实最后一个字符的存储模式和前面的没任何区别,只是要少数$4-m\%4$个二进制位而已)。然后接下来一个$q$,表示用户的个数。接下$q$个数字,表示$q$个用户的id。

  题目要计算的是,有多少个数据文件的记录包含至少一个用户。一个数据文件记录包含一个用户的含义是:假设数据文件记录为$R$,用户的id为$u$,如果对于所有哈希函数的参数$a_i(0 \le i < f)$,通过哈希函数$j = (u * a_i)\ \% m$得到的所有$j$,记录$R$的第$j$位都是$1$,就说明这个数据文件记录包含这个用户。

二、思路

  搞懂题意后,预处理出每一个用户的所有$j$,放到bitset里面(不是必须,数组也可以。只是后面比较起来复杂度会大一点而已。),假设是$b$;然后枚举每一个数据文件记录$R$,再枚举每一个用户的$b$,如果$R\ \&\ b\ ==\ b$,说明记录$b$的每一个是$1$的二进制位在$R$中都是$1$,那么,这个数据文件是满足题目要求的。

  注意范围,$a_i$和$u_k$都是$[1, 2^{31})$,别相乘后溢出了。

三、源代码

#include <bits/stdc++.h>
using namespace std;
#define MAXN 1024
typedef long long LL;
int m, f, n, q, a[MAXN];
char str[MAXN][MAXN / 4 + 10];
bitset<MAXN> df[MAXN], uh[MAXN];
vector<int> ans;
int main() {
    while(~scanf("%d%d", &m, &f)) {
        for(int i = 0; i < MAXN; ++i)df[i].reset();
        for(int i = 0; i < MAXN; ++i)uh[i].reset();
        ans.clear();
        for(int i = 0; i < f; ++i)scanf("%d", a + i);
        scanf("%d", &n);
        for(int i = 0; i < n; ++i) {
            scanf("%s", str[i]);
            int len = strlen(str[i]);
            for(int j = 0; j < len; ++j) {
                int t = isdigit(str[i][j]) ? str[i][j] - '0' : str[i][j] - 'a' + 10;
                if(j == len - 1 && m % 4 != 0) {
                    if(m % 4 == 1) {
                        if(t & (1 << 0))df[i].set(j * 4 + 0);
                    }
                    if(m % 4 == 2) {
                        if(t & (1 << 0))df[i].set(j * 4 + 0);
                        if(t & (1 << 1))df[i].set(j * 4 + 1);
                    }
                    else if(m % 4 == 3) {
                        if(t & (1 << 0))df[i].set(j * 4 + 0);
                        if(t & (1 << 1))df[i].set(j * 4 + 1);
                        if(t & (1 << 2))df[i].set(j * 4 + 2);
                    }
                }
                else {
                    if(t & (1 << 0))df[i].set(j * 4 + 0);
                    if(t & (1 << 1))df[i].set(j * 4 + 1);
                    if(t & (1 << 2))df[i].set(j * 4 + 2);
                    if(t & (1 << 3))df[i].set(j * 4 + 3);
                }

            }
            //cout << df[i] << endl;
        }
        scanf("%d", &q);
        LL u;
        for(int i = 0; i < q; ++i) {
            cin >> u;
            for(int j = 0; j < f; ++j) {
                uh[i].set((u * a[j]) % m);
            }
        }
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < q; ++j) {
                if((df[i]&uh[j]) == uh[j]) {
                    ans.push_back(i);
                    break;
                }
            }
        }
        if(ans.size() == 0)printf("%d\n", ans.size());
        else printf("%d ", ans.size());
        for(int i = 0; i < ans.size(); ++i)printf("%d%c", ans[i], i == int(ans.size()) - 1 ? '\n' : ' ');
    }
    return 0;
}

I-Improvements

J-Jokewithpermutation (通过)

一、思路

  <错误的做法>

  统计每一个数字在字符串中的出现次数。那么,必定存在至少一个数字的出现次数是1。把这些出现次数是1的数字都push到队列中。然后,用类似于bfs的方式处理:取出队列的头元素e,找出它在字符串中的出现位置(不预处理也可以,每次都搜索一遍),然后,它影响的数字最多有四个。比如,全排列字符串为$...1234...$,我们取出的队列头元素是$23$,那么,数字$12$、$2$、$3$、$34$的出现次数都减少了$1$。当然,如果取出的数字是$1$位数,那么,它只影响三位(写代码的时候被这个细节坑了好多发RE的)。如果被$e$影响的数字次数为$1$了(被影响的数字肯定出现次数大于$1$),把它放到优先队列里面。同时把元素$e$在字符串中的出现位置上替换成#字符。

  最后,当剩余的所有数字的出现次数都大于$1$时,我天真地得出这么个结论(错的):此时序列必定满足...###111···###...###222···###...这种形式。由公式$n\ =\ len\ >\ 9\ ?\ \frac{len-9}{2}\ +\ 9\ :\ len$可计算出$n$,从$n$到$1$枚举每一个没在队列中出现过的数字,

  <正确的做法>

  dfs暴力搜索即可。只要剪枝用的好,没有题目A不了。

K-Knockout Racing (通过)

<qj>

思路:

n*k也就1e6,直接暴力呀。

计算时要注意a和b的关系,如果a在b的左边,是一种做法;

如果a在b的右边,是另一种做法。

设 l = abs(a-b)

那么在t时间内,已经完整走过了time =  t/l 次,剩下rest = t%l ,这时候判断 t/l 的奇偶性,然后判断起始方向便可。

原文地址:https://www.cnblogs.com/dowhile0/p/8726861.html