头像

Except1on




离线:2天前


最近来访(16)
用户头像
shiyasai
用户头像
Eyjafjalla
用户头像
uaN_1
用户头像
火车驶向云外
用户头像
当橘者迷
用户头像
不错呆毛
用户头像
theyoung
用户头像
yxc的小迷妹
用户头像
懒惰的蚩蚩
用户头像
有机物
用户头像
Lims
用户头像
Lims
用户头像
馨意
用户头像
我爱y总


这是我考试时写的代码,原封不动搬过来(CSP居然可以下载答卷!)
CSP:6.765s,55.78MB

search前面的部分是字符串解析,把表达式存到vector里,省得后面重复遍历字符串(PS:这题的括号不影响运算优先级)
search遍历所有用户,检查是否符合表达式条件。表达式用一个栈处理就行,最后栈顶元素就是表达式结果。
最后排个序输出即可

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <sstream>
#include <stack>
#include <unordered_map>
#include <algorithm>
using namespace std;

const int N = 2510;
const int M = 510;

unordered_map<int, int> attr[N];
int n, m;
int selected[N];
int sel_cnt;

string expr;

typedef struct ATOM
{
    int a, b;
    char op;
} atom, exp;

int main()
{
    scanf("%d", &n);
    for (int i = 1;  i <= n; i++)
    {
        int dn, n_attr, attr_n, attr_v;
        scanf("%d", &dn);
        attr[i][0] = dn;
        scanf("%d", &n_attr);
        for (int j = 1; j <= n_attr; j++)
        {
            scanf("%d%d", &attr_n, &attr_v);
            attr[i][attr_n] = attr_v;
        }
    }

    scanf("%d", &m);
    vector<exp> v;
    for (int i = 1; i <= m; i++)
    {
        cin >> expr;

        // atom
        if (expr[0] != '&' && expr[0] != '|')
        {
            for (int i = 0; i < expr.size(); i++)
            {
                if (expr[i] == ':' || expr[i] == '~')
                {
                    string num1_s = expr.substr(0, i);
                    string num2_s = expr.substr(i + 1);
                    int num1_i, num2_i;
                    stringstream ss;
                    ss << num1_s;
                    ss >> num1_i;
                    ss.clear();
                    ss << num2_s;
                    ss >> num2_i;
                    atom at;
                    at.a = num1_i;
                    at.b = num2_i;
                    at.op = expr[i];
                    v.push_back(at);
                    break;
                }
            }


        }
        else
        {
            for (int i = 0; i < expr.size(); i++)
            {
                if (expr[i] == '&' || expr[i] == '|')
                {
                    exp e;
                    e.op = expr[i];
                    v.push_back(e);
                }
                else if (expr[i] >= '0' && expr[i] <= '9')
                {
                    // atom
                    int j = i + 1;
                    int t = i + 1;
                    while (expr[j] != ')')
                    {
                        if (expr[j] == ':' || expr[j] == '~')
                            t = j;
                        j++;
                    }

                    string num1_s = expr.substr(i, t - i);
                    string num2_s = expr.substr(t + 1, j - t - 1);
                    int num1_i, num2_i;
                    stringstream ss;
                    ss << num1_s;
                    ss >> num1_i;
                    ss.clear();
                    ss << num2_s;
                    ss >> num2_i;
                    atom at;
                    at.a = num1_i;
                    at.b = num2_i;
                    at.op = expr[t];
                    v.push_back(at);

                    i = j;
                }
            }
        }


        // search
        for (int i = 1;  i <= n; i ++)
        {
            bool flag = true;
            stack<bool> stk;
            for (int j = v.size() - 1;  j >= 0; j--)
            {
                exp &current = v[j];
                if (current.op == ':')
                {
                    if (attr[i].find(current.a) == attr[i].end() || attr[i][current.a] != current.b)
                        stk.push(false);
                    else
                        stk.push(true);
                }
                if (current.op == '~')
                {
                    if (attr[i].find(current.a) == attr[i].end() || attr[i][current.a] == current.b)
                        stk.push(false);
                    else
                        stk.push(true);
                }
                if (current.op == '&')
                {
                    bool num1 = stk.top();
                    stk.pop();
                    bool num2 = stk.top();
                    stk.pop();
                    stk.push(num1 && num2);
                }
                if (current.op == '|')
                {
                    bool num1 = stk.top();
                    stk.pop();
                    bool num2 = stk.top();
                    stk.pop();
                    stk.push(num1 || num2);
                }
            }

            if (stk.top())
            {
                sel_cnt++;
                selected[sel_cnt] = attr[i][0];
            }
        }

        sort(selected + 1, selected + sel_cnt + 1);
        for (int i = 1; i <= sel_cnt; i++)
            printf("%d ", selected[i]);
        printf("\n");

        sel_cnt = 0;
        vector<exp> empty_v;
        v.swap(empty_v);
    }
    return 0;
}



Except1on
3个月前
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
using LL = long long;

const int N = 100010;

int n;
LL w[N];
LL s[N];

int main()
{
    scanf("%d", &n);
    LL sum = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld%lld", &w[i], &s[i]);
        sum += w[i] * s[i];
    }
    LL zero = 0;
    printf("%lld\n", max(zero, sum));
    return 0;
}


活动打卡代码 AcWing 3411. 灰度直方图

Except1on
3个月前
#include <iostream>
#include <cstdio>
using namespace std;

const int N = 300;

int n, m;
int L;
int h[N];

int main()
{
    scanf("%d%d%d", &n, &m, &L);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            int x;
            scanf("%d", &x);
            h[x]++;
        }
    }

    for (int i = 0; i < L; i++)
        printf("%d ", h[i]);
    printf("\n");
    return 0;
}



活动打卡代码 AcWing 4008. 脉冲神经网络

Except1on
4个月前

要开O2优化,否则过不了最后一个数据
(反正CSP能满分,不改了)

#pragma GCC optimize(2)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <deque>
#include <cstring>
#include <vector>
using namespace std;

int N, S, P, T;
double delta;

class Neure
{
public:
    double v, u;
    double a, b, c, d;
    int cnt = 0;

public:
    void set(double v, double u, double a, double b, double c, double d)
    {
        this->v = v;
        this->u = u;
        this->a = a;
        this->b = b;
        this->c = c;
        this->d = d;
    }

    bool update(double I)
    {
        double old_v = v;
        v = v + delta * (0.04 * v * v + 5 * v + 140 - u) + I;
        u = u + delta * a * (b * old_v - u);

        if (v >= 30)
        {
            // 发射脉冲
            v = c;
            u = u + d;
            cnt++;
            return true;
        }
        return false;
    }
};

Neure cell[1010];
double input[1010][1010];
int r[1010];

class Cynapse
{
public:
    int start, end;
    double w;
    int D;

public:
    void set(int start, int end, double w, int D)
    {
        this->start = start;
        this->end = end;
        this->w = w;
        this->D = D;
    }
};

Cynapse cynapse[1010];
vector<int> as_start[2010]; // as_start[i]:以i号神经元/i-N号脉冲源为起点的突触

static unsigned long _next = 1;

/* RAND_MAX assumed to be 32767 */
int myrand(void)
{
    _next = _next * 1103515245 + 12345;
    return ((unsigned)(_next / 65536) % 32768);
}

int main()
{
    scanf("%d%d%d%d", &N, &S, &P, &T);
    scanf("%lf", &delta);
    int r_sum = 0;
    while (r_sum != N)
    {
        int r;
        double u, v, a, b, c, d;
        scanf("%d%lf%lf%lf%lf%lf%lf", &r, &v, &u, &a, &b, &c, &d);
        for (int i = 0; i < r; i++)
            cell[i + r_sum].set(v, u, a, b, c, d);
        r_sum += r;
    }
    for (int i = 0; i < P; i++)
        scanf("%d", &r[i]);
    int mod = 0;
    for (int i = 1; i <= S; i++)
    {
        int s, t, D;
        double w;
        scanf("%d%d%lf%d", &s, &t, &w, &D);
        cynapse[i].set(s, t, w, D);
        as_start[s].push_back(i);
        mod = max(mod, D + 1);
    }

    for (int i = 0; i < T; i++)
    {
        int t = i % mod;
        for (int j = 0; j < N; j++)
            if (cell[j].update(input[t][j]))
                for (int k = 0; k < as_start[j].size(); k++)
                {
                    Cynapse &cy = cynapse[as_start[j][k]];
                    input[(t + cy.D) % mod][cy.end] += cy.w;
                }
        for (int j = 0; j < P; j++)
            if (r[j] > myrand())
                for (int k = 0; k < as_start[j + N].size(); k++)
                {
                    Cynapse &cy = cynapse[as_start[j + N][k]];
                    input[(t + cy.D) % mod][cy.end] += cy.w;
                }
        memset(input[t], 0, sizeof(input[t]));
    }

    double v_max = -0x3f3f3f3f, v_min = 0x3f3f3f3f;
    int cnt_max = -0x3f3f3f3f, cnt_min = 0x3f3f3f3f;
    for (int i = 0; i < N; i++)
    {
        v_max = max(v_max, cell[i].v);
        v_min = min(v_min, cell[i].v);
        cnt_max = max(cnt_max, cell[i].cnt);
        cnt_min = min(cnt_min, cell[i].cnt);
    }
    printf("%.3lf %.3lf\n%d %d\n", v_min, v_max, cnt_min, cnt_max);
    return 0;
}



Except1on
4个月前

解题思路

首先观察样例:3 1 2 0 0 2 0 4 5 0 2,初始状态共有非零段4个。

然后我们从小到大将里面的数字置0:
1. 将1置0,得3 0 2 0 0 2 0 4 5 0 2,非零段个数为5
2. 将2值0,得3 0 0 0 0 0 0 4 5 0 0,非零段个数为2
3. 将3值0,得0 0 0 0 0 0 0 4 5 0 0,非零段个数为1
4. 将4值0,得0 0 0 0 0 0 0 0 5 0 0,非零段个数为1
5. 将5值0,得0 0 0 0 0 0 0 0 0 0 0,非零段个数为0

观察上述过程,可以发现,当我们将数组的某个数字置0时,无非3种情况:
1. 这个数字不是非零段的边界,将这个数置0后,会使非零段总数加1,例如样例的1
2. 这个数字是非零段的边界,且该非零段只有这1个数字,将这个数置0后,会使非零段总数减1,例如样例的25
3. 这个数字是非零段的边界,但该非零段还存在其他数字,将这个数置0后,非零段总数不变,例如样例的4

那么我们就可以从小到大遍历数组$A$,一次将数字置0,每次置0时根据上述3条规则修改非零段总数,记录最大的非零段总数即可

每次置0后,数组的非零段边界会发生变化,需要注意更新

为了实现从小到大遍历数组$A$,我通过vector<int> index[M]来记录每个数的所有下标,其中index[x]表示数字$x$在数组$A$中的所有下标

时间复杂度

遍历1次数组$A$,因此时间复杂度为$O(n)$
代码中虽然用了两层循环,但是实际上只遍历了1次数组$A$。不过i从a_min遍历到a_max会带来额外的计算开销(很小!),因此可以用一个数组记录所有出现过的数字,并从小到大排序,然后让i遍历这个数组

C++代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 500010;
const int M = 10010;

int n;
int a[N];
vector<int> index[M];
bool is_edge[N];
int cnt;
int cnt_max;

int main()
{
    scanf("%d", &n);
    int a_min = 0x3f3f3f3f, a_max = -1;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        if (a[i] == 0)
        {
            is_edge[i] = false;
            if (i > 1 && a[i - 1] != 0)
            {
                is_edge[i - 1] = true; // 右边界
                cnt++;
            }
        }
        else
        {
            if (i > 1 && a[i - 1] == 0)
                is_edge[i] = true; // 左边界
            if (i == 1)
                is_edge[i] = true; // 左边界
            if (i == n)
            {
                is_edge[i] = true; // 右边界
                cnt++;
            }
        }
        index[a[i]].push_back(i);
        a_min = min(a_min, max(a[i], 1)); // max(a, 1)过滤掉0
        a_max = max(a_max, a[i]);
    }
    cnt_max = cnt;

    for (int i = a_min; i <= a_max; i++)
    {
        for (int j = 0; j < index[i].size(); j++)
        {
            int idx = index[i][j];
            if (is_edge[idx] == false)
            {
                // 置0的位置不是边界,那么这个数被置0后,会使得非零段个数+1,并且idx的前后变成新的非零段边界
                cnt++;
                is_edge[idx + 1] = true;
                is_edge[idx - 1] = true;
            }
            else if (a[idx - 1] <= i && a[idx + 1] < i) // 如果a[idx-1]==i,那么a[idx-1]在上次循环中已经被置0了
                cnt--;                                  // 置0位置的前后都已经被置0,那么这个数被置0后,会使得非零段个数-1
            else if (a[idx - 1] > i)
                is_edge[idx - 1] = true;
            else
                is_edge[idx + 1] = true;
            is_edge[idx] = false;
        }
        cnt_max = max(cnt_max, cnt);
    }
    printf("%d\n", cnt_max);
    return 0;
}


活动打卡代码 AcWing 4007. 非零段划分

Except1on
4个月前
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 500010;
const int M = 10010;

int n;
int a[N];
vector<int> index[M];
bool is_edge[N];
int cnt;
int cnt_max;

int main()
{
    scanf("%d", &n);
    int a_min = 0x3f3f3f3f, a_max = -1;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        if (a[i] == 0)
        {
            is_edge[i] = false;
            if (i > 1 && a[i - 1] != 0)
            {
                is_edge[i - 1] = true; // 右边界
                cnt++;
            }
        }
        else
        {
            if (i > 1 && a[i - 1] == 0)
                is_edge[i] = true; // 左边界
            if (i == 1)
                is_edge[i] = true; // 左边界
            if (i == n)
            {
                is_edge[i] = true; // 右边界
                cnt++;
            }
        }
        index[a[i]].push_back(i);
        a_min = min(a_min, max(a[i], 1)); // max(a, 1)过滤掉0
        a_max = max(a_max, a[i]);
    }
    cnt_max = cnt;

    for (int i = a_min; i <= a_max; i++)
    {
        for (int j = 0; j < index[i].size(); j++)
        {
            int idx = index[i][j];
            if (is_edge[idx] == false)
            {
                // 置0的位置不是边界,那么这个数被置0后,会使得非零段个数+1,并且idx的前后变成新的非零段边界
                cnt++;
                is_edge[idx + 1] = true;
                is_edge[idx - 1] = true;
            }
            else if (a[idx - 1] <= i && a[idx + 1] < i) // 如果a[idx-1]==i,那么a[idx-1]在上次循环中已经被置0了
                cnt--;                                  // 置0位置的前后都已经被置0,那么这个数被置0后,会使得非零段个数-1
            else if (a[idx - 1] > i)
                is_edge[idx - 1] = true;
            else
                is_edge[idx + 1] = true;
            is_edge[idx] = false;
        }
        cnt_max = max(cnt_max, cnt);
    }
    printf("%d\n", cnt_max);
    return 0;
}


活动打卡代码 AcWing 4006. 数组推导

Except1on
4个月前
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

int main()
{
    int n, sum_max = 0, sum_min = 0;
    int b0 = 0, b1 = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &b1);
        if (b1 != b0)
            sum_min += b1;
        sum_max += b1;
        b0 = b1;
    }
    printf("%d\n%d\n", sum_max, sum_min);
    return 0;
}


活动打卡代码 AcWing 4281. 序列查询新解

Except1on
4个月前

思路

基本思路:构建数组$g(i)-f(i)$,然后对该数组取绝对值再求和即可

观察样例1解释的表格可以看出,$f(i)$和$g(i)$的值在一定区间范围内是相同的,此时再想到差分的作用:以$O(1)$的复杂度对数组的某个区间加上或减去同一个值,就可以知道我们可以通过差分来构建$g(i)-f(i)$!

具体的差分方法为:
1. 将差分数组置0
2. 找到$g(i)$的各个区间,并依次插入$1, 2, 3,…$
3. 找到$f(i)$的各个区间,并依次插入$-1, -2, -3,…$
4. 得到数组$g(i)-f(i)$的差分$diff(i)$

但是要得到最终的$g(i)-f(i)$,我们需要对$diff(i)$求和,而i的数量级达到了$10^9$,从0开始求和显然会超时,空间也不允许开这么大的数组。
此时可以用hash来解决:只对出现过的区间边界求和,对应代码的exist_index。出现过的区间边界的数量是非常少的,具体见时间复杂度分析。

时间复杂度

配合代码食用风味更佳
1. 对于计算$g(i)$差分的for循环,有$i<\frac{N}{r}=N\cdot\frac{n+1}{N}=n+1$,复杂度$O(n)$,同时exist_index长度最多增加n
2. 对于计算$g(i)-f(i)$差分的for循环,时间复杂度显然为$O(n)$,同时exist_index长度最多增加n
3. 最后遍历exist_index,其长度最长为2n,因此时间复杂度$O(n)$

最终时间复杂度为$O(n)$

C++代码

#include <cstdio>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;

const int MAX = 100010;

int n, N;
int r;
LL a[MAX];
unordered_map<LL, LL> diff;         // 因为N达到10^9,所以用unordered_map表示差分数组
unordered_map<LL, bool> used_index; // 标记差分中哪些下标被使用过
vector<LL> exist_index;             // 不重复地存储差分中被使用过的下标(见insert)

void insert(LL l, LL r, LL c)
{
    diff[l] += c;
    diff[r + 1] -= c;
    if (used_index.find(l) == used_index.end())
    {
        used_index[l] = true;
        exist_index.push_back(l);
    }
    if (used_index.find(r + 1) == used_index.end())
    {
        used_index[r + 1] = true;
        exist_index.push_back(r + 1);
    }
}

int main()
{
    scanf("%d%d", &n, &N);
    r = N / (n + 1);

    // 计算g(i)的差分
    for (int i = 0; i * r < N; i++)
        insert(i * r, min((i + 1) * r - 1, N - 1), i);

    // 计算g(i)-f(i)的差分(此时还没加绝对值)
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        insert(a[i - 1], a[i] - 1, 1 - i); // -(i-1)=1-i
    }
    insert(a[n], N - 1, -n);

    // 差分求前缀和得到原始g(i)-f(i),并加绝对值
    // 根据区间长度(即exist_index[i + 1] - exist_index[i])加权求和,得到error
    sort(exist_index.begin(), exist_index.end());
    LL error = 0, sum = 0;
    for (int i = 0; i < exist_index.size() - 1; i++)
    {
        sum = sum + diff[exist_index[i]]; // 前缀和还原g(i)-f(i),无绝对值
        error = error + (exist_index[i + 1] - exist_index[i]) * abs(sum);
    }
    printf("%lld\n", error);
    return 0;
}



Except1on
4个月前

思路

基本思路:构建数组$g(i)-f(i)$,然后对该数组取绝对值再求和即可

观察样例1解释的表格可以看出,$f(i)$和$g(i)$的值在一定区间范围内是相同的,此时再想到差分的作用:以$O(1)$的复杂度对数组的某个区间加上或减去同一个值,就可以知道我们可以通过差分来构建$g(i)-f(i)$!

具体的差分方法为:
1. 将差分数组置0
2. 找到$g(i)$的各个区间,并依次插入$1, 2, 3,…$
3. 找到$f(i)$的各个区间,并依次插入$-1, -2, -3,…$
4. 得到数组$g(i)-f(i)$的差分$diff(i)$

但是要得到最终的$g(i)-f(i)$,我们需要对$diff(i)$求和,而i的数量级达到了$10^9$,从0开始求和显然会超时,空间也不允许开这么大的数组。
此时可以用hash来解决:只对出现过的区间边界求和,对应代码的exist_index。出现过的区间边界的数量是非常少的,具体见时间复杂度分析。

时间复杂度

配合代码食用风味更佳
1. 对于计算$g(i)$差分的for循环,有$i<\frac{N}{r}=N\cdot\frac{n+1}{N}=n+1$,复杂度$O(n)$,同时exist_index长度最多增加n
2. 对于计算$g(i)-f(i)$差分的for循环,时间复杂度显然为$O(n)$,同时exist_index长度最多增加n
3. 最后遍历exist_index,其长度最长为2n,因此时间复杂度$O(n)$

最终时间复杂度为$O(n)$

C++代码

#include <cstdio>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;

const int MAX = 100010;

int n, N;
int r;
LL a[MAX];
unordered_map<LL, LL> diff;         // 因为N达到10^9,所以用unordered_map表示差分数组
unordered_map<LL, bool> used_index; // 标记差分中哪些下标被使用过
vector<LL> exist_index;             // 不重复地存储差分中被使用过的下标(见insert)

void insert(LL l, LL r, LL c)
{
    diff[l] += c;
    diff[r + 1] -= c;
    if (used_index.find(l) == used_index.end())
    {
        used_index[l] = true;
        exist_index.push_back(l);
    }
    if (used_index.find(r + 1) == used_index.end())
    {
        used_index[r + 1] = true;
        exist_index.push_back(r + 1);
    }
}

int main()
{
    scanf("%d%d", &n, &N);
    r = N / (n + 1);

    // 计算g(i)的差分
    for (int i = 0; i * r < N; i++)
        insert(i * r, min((i + 1) * r - 1, N - 1), i);

    // 计算g(i)-f(i)的差分(此时还没加绝对值)
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        insert(a[i - 1], a[i] - 1, 1 - i); // -(i-1)=1-i
    }
    insert(a[n], N - 1, -n);

    // 差分求前缀和得到原始g(i)-f(i),并加绝对值
    // 根据区间长度(即exist_index[i + 1] - exist_index[i])加权求和,得到error
    sort(exist_index.begin(), exist_index.end());
    LL error = 0, sum = 0;
    for (int i = 0; i < exist_index.size() - 1; i++)
    {
        sum = sum + diff[exist_index[i]]; // 前缀和还原g(i)-f(i),无绝对值
        error = error + (exist_index[i + 1] - exist_index[i]) * abs(sum);
    }
    printf("%lld\n", error);
    return 0;
}


活动打卡代码 AcWing 4280. 序列查询

Except1on
4个月前
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;

const int MAX = 210;

int n, N;
LL a[MAX];
LL sum;

int main()
{
    scanf("%d%d", &n, &N);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        sum += (a[i] - a[i - 1]) * (i - 1);
    }
    sum += (N - a[n]) * n;
    printf("%lld\n", sum);
    return 0;
}