zoukankan      html  css  js  c++  java
  • 省选模拟测试7

    期望得分: (100+70+30 = 200)

    实际得分:(70+0+0 = 70)

    (md) 今天挂了好多分,难受。

    (T1) 打了个 (set) 的假做法,常数巨大,被卡成了 (70) 分。

    (T2)(tm) (Hash) 是关键字,(70) 分直接被干没了。

    (T3) (ynoi) 的题,打了 (30) 分的暴力,但没开 ( unsigned long long) 直接炸了。

    T1​ 发微博

    题意描述

    刚开通的 SH 微博共有 (n) 个用户((1sim n) 标号),在这短短一个月的时间内,用户们活动频繁,共有 (m) 条按时间顺序的记录:

    ! x 表示用户 x 发了一条微博;
    + x y 表示用户 x 和用户 y 成为了好友
    − x y 表示用户 x 和用户 y 解除了好友关系
    

    当一个用户发微博的时候,所有他的好友(直接关系)都会看到他的消息。

    假设最开始所有人之间都不是好友关系,记录也都是合法的(即 + x y(x)(y) 一定不是好友,而 − x y(x)(y) 一定是好友)。

    问这 (m) 条记录发生之后,每个用户分别看到了多少条消息

    数据范围:(nleq 2 imes 10^5,mleq 10^6)

    solution

    洛谷原题

    可以直接拿 (set) 正着做,下面有一个比较好写的做法。

    考虑如果一条关系 (x,y) 出现的时间为 (l,r) 那么 (x) 答案就要累加上 (y) 在这一段时间内发布的微博数。

    正着维护前缀和的话,空间开不下。

    考虑倒着做,维护一个 (sum[x]) 数组,表示从 (i-n) 这一段时间内 (x) 发送的微博数。

    如果 (x,y) 在这一刻成为了好友,就让 ans[x] += sum[y],ans[y]+= sum[x]

    反之解除好友就把 ans[x]-=sum[y],ans[y]-=sum[x]

    然后这道题就做完了。复杂度 (O(n))

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    #include<set>
    using namespace std;
    #define mp make_pair
    const int N = 5e5+10;
    int n,m,x,y,tot,w[N],sum[N];
    struct node
    {
    	int opt,x,y;
    }q[N];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int main()
    {
        n = read(); m = read();
        for(int i = 1; i <= m; i++)
        {
            char ch; cin>>ch;
            if(ch == '!')
            {
                q[i].x = read();
                q[i].opt = 1;
            }
            if(ch == '+')
            {
            	q[i].x = read();
            	q[i].y = read();
            	q[i].opt = 2;
            }
            if(ch == '-')
            {
            	q[i].x = read();
            	q[i].y = read();
            	q[i].opt = 3;
            }
        }
        for(int i = m; i >= 1; i--)
        {
        	if(q[i].opt == 1) sum[q[i].x]++;
        	else if(q[i].opt == 2) w[q[i].x] += sum[q[i].y], w[q[i].y] += sum[q[i].x];
        	else if(q[i].opt == 3) w[q[i].x] -= sum[q[i].y], w[q[i].y] -= sum[q[i].x];
    	}
        for(int i = 1; i <= n; i++) printf("%d ",w[i]);
        printf("
    ");
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2 字符串

    题意描述

    (ysy) 不想让家长看自己的聊天记录,所以 (ysy) 就想出了一套密码以及一种加密方式。

    ​ 加密方式:现在给出一段英文,我们把每一个单词翻转,并将其所有的大写字母都变为小写字母,最后在把这些单词收尾相接得到一个字符串,这样我们就加密完了。举个例子 (ab Aes Ksd):加密后变为 (baseadsk)

    ​ 坐在电脑屏幕另一侧的你对于 (ysy) 这样的行为十分恼怒,因为看不懂,但是好在 (ysy) 给你了他所有可能说的单词,现在你需要运用编程能力将 (ysy) 说的话解密。

    数据范围:字符串长度 (nleq 10000), 单词个数 (mleq 5000), 所有单词长度 (lenleq 1000)

    solution

    首先有 (70) 分的 (O(n^2)) 做法,就是设 (f[i]) 表示前 (i) 个字符能否拼接成。

    转移时枚举每个单词,如果 (s(j+1,i)) 和单词相同,则 (f[i] |= f[j])

    同时记录一下决策点,来输出方案。

    判断两个字符串是否相同,用 (Hash) 来判断即可。

    其实我们是没必要枚举每个单词的,因为单词的长度是小于 (1000) 的,所以我们只需要枚举 (s(1,i)) 长度小于 (1000) 的后缀即可。

    众所周知 map[x] 非常慢,所以建议使用 map.find(x)

    复杂度: (O(n1000logm))

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<map>
    using namespace std;
    #define ull unsigned long long
    const int N = 1e5+10;
    const int base = 23333;
    const int p = 998244353;
    int n,m,cnt;
    int pos[N],f[N],pre[N],len[N];
    ull has[N];
    char s[N],b[100010][1010];
    map<ull,int> id;
    int main()
    {
    	freopen("char.in","r",stdin);
    	freopen("char.out","w",stdout);
        scanf("%d",&n); scanf("%s",s+1);
        scanf("%d",&m); 
        for(int i = 1; i <= m; i++)
        {
            scanf("%s",b[i]+1);
            int k = strlen(b[i]+1);
    	ull tmp = 0;
            for(int j = k; j >= 1; j--) 
    	{
    		if(b[i][j] < 'a') tmp = tmp * base + b[i][j] - 'A' + 'a';
    		else tmp = tmp * base + b[i][j];
    	}
            if(id.find(tmp) == id.end()) id[tmp] = i; 
    	len[i] = k;
        }
        f[0] = 1;
        for(int i = 1; i <= n; i++)
        {
        	ull tmp = 0, mi = 1;
            for(int j = i; j >= 1 && i-j+1 <= 1000; j--)
            {
                tmp = tmp + s[j] * mi; mi = mi * base;
                if(f[i]) break;
                if(id.find(tmp) != id.end() && f[j-1] == 1)
                {
                    f[i] = 1;
                    pre[i] = id[tmp];
                    break;
                }
            }
        }
        while(n)
        {
            pos[++cnt] = pre[n];
            n = n-len[pre[n]];
        }
        for(int i = cnt; i >= 1; i--) printf("%s ",b[pos[i]]+1);
        printf("
    ");
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T3 序列/由乃的OJ

    题意描述

    给你一个有 (n)个点的树,每个点的包括一个位运算 (opt) 和一个权值 (x),位运算有 &|^ 三种,分别用 $1,2,3$1, 表示。

    每次询问包含三个整数 (x,y,z),初始选定一个数 (v)。然后 (v) 依次经过从 (x)(y) 的所有节点,每经过一个点 (i) (v) 就变成 (v opt_i x_i) ,所以他想问你,最后到 (y) 时,希望得到的值尽可能大,求最大值。给定的初始值 (v) 必须是在 ([0,z])[之间。

    每次修改包含三个整数 (x,y,z) ,意思是把 (x) 点的操作修改为 (y),数值改为 (z)

    数据范围: (n,mleq 10^5, 0leq kleq 64)

    solution

    这个其实是起床困难综合征的树上版本。

    不难想到用线段树来维护。

    考虑对线段树上的每一个区间维护 (4) 个值 (l_0,l_1,r_0,r_1) 分别表示,每一位全为 (0 /1) 的数,从左/右依次进行运算得到的结果。

    咕咕咕。

  • 相关阅读:
    django-orm基础字段及选项1
    django-模型层
    django-应用及分布式路由
    django-static配置静态文件
    django-url反向解析
    django-url路径书写规范
    django-模板继承 block、endblock、 extend
    详解HTML中的表单元素
    详解HTML中的表格标签
    jQuery和AJAX基础
  • 原文地址:https://www.cnblogs.com/genshy/p/14502128.html
Copyright © 2011-2022 走看看