2019/11/1 CSP模拟

写在前面的反思

该拿的部分分还是得拿完啊,因为懒+动作慢没有写最后一道题的菊花图和链的情况,其实这两个点并不难。。
虽然只有\(10pts\),但是已经足够往上爬一截了啊,额外的\(10pts\)在今天大众分\(210pts\)的背景下显得好重要
另外\(T2\)下来发现最后判断的地方假了,所幸好像它能跑得动的数据范围内都没出问题,但要卡还是很好卡,只是这次运气好没被卡而已,下次写的时候还是要注意,正式比赛不一定就不卡了

T1

\(sb\)题
对于\(b==1\),判断所有总数的\(gcd\)能不能整除\(a\)
对于\(b == 2\),判断总数中最小的一个开方是不是严格大于\(a\)
\(sqrt()\)的返回值是浮点数救我一命,差点想乘起来了,发现会爆\(long\ long\)

T2

思路

桶套桶

记录标本串每个字母出现的次数(第一层),设每个字母出现的次数为\(cnt_i\),那么第二层以\(cnt_i\)为下标,统计每个字母出现次数的次数,记为\(cnt1_i\)

举个例子

原标本串

\(aabbbcccd\)

第一层桶,下标对应字母:\(2\ 3\ 3\ 1\)

第二层桶,下标对应第一层桶中的值:\(1\ 1\ 3\)

考虑记录一个\(tot\)总和,当我们当前枚举到的子串标本串的第二层桶的各项差的绝对值之和为\(0\)时,这两者第二层桶中的每个值一一对应,即可以通过变换得到

因为每次挪动区间类似滑动窗口,所以我们只需要处理最开始的区间,之后的区间减去左边挪掉的端点,加入右端新进入的端点即可继续统计答案

代码:

#include<bits/stdc++.h>
#define N (2000 + 10)
using namespace std;
inline int read() {
    int cnt = 0, f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
    while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
    return cnt * f;
}
int T, n, Q, cnt[30], cnt1[30], g[N], f[N], q[N][30], l, r, ans, tmp, len, tot;
bool vis[N];
char ch; int s[N];
void add(int x, int sig) {
    /*
        因为挪动不关标本串的事,所以一切处理都在cnt1和f数组上,g只拿来比较
    */
    if (f[cnt1[x]] > g[cnt1[x]]) --tot; else ++tot;  //因为要减去字母x原来出现次数的影响,所以同样影响到tot,把tot被影响的部分减掉
    --f[cnt1[x]]; //外层桶减去字母x原来出现次数的影响
    cnt1[x] += sig; //+1或-1,看是删除还是添加,更新内层桶
    if (f[cnt1[x]] >= g[cnt1[x]]) ++tot; else --tot; //更新外层桶,和上面减的地方类似
    ++f[cnt1[x]]; //外层桶加上字母x新出现次数的影响
}
int main() {
    T = read();
    while (T--) {
        n = read(), Q = read();
        tmp = 0;
        for (; !isalpha(ch); ch = getchar());
        for (; isalpha(ch); ch = getchar()) s[++tmp] = ch - 'a' + 1;
        for (register int i = 1; i <= n; ++i) {  //整个串,每个字母出现次数的前缀和统计
            for (register int j = 1; j <= 26; ++j) q[i][j] = q[i - 1][j]; //把前一个的答案继承过来,每个字母都要继承
            ++q[i][s[i]]; //把这个字母的答案统计进去
        } while (Q--) {
            /*
                cnt: 标本串的内层桶 cnt2:枚举区间的内层桶
                g:标本串的外层桶 f:枚举区间的外层桶
            */
            for (register int i = 1; i <= 26; ++i) cnt[i] = cnt1[i] = 0;  //内层桶初始化
            l = read(), r = read();
            len = r - l + 1;
            //当前在处理区间[1, len]这个子串(当前枚举的区间),即对于每个询问枚举的第一个区间
            for (register int i = 1; i <= 26; ++i) {
                cnt[i] = q[r][i] - q[l - 1][i]; //处理标本串的每个字母的出现次数,前缀和减一下
                cnt1[i] = q[len][i];  //处理当前子串的每个字母的出现次数
                ++g[cnt[i]];  //对标本串字母出现次数的出现次数(外层桶)进行统计
                ++f[cnt1[i]]; //对当前子串...............进行统计(中间同上)
            }
            tot = 0;  //总和,初始化为0
            for (register int i = 1; i <= 26; ++i)
                if (!vis[cnt[i]]) vis[cnt[i]] = true, tot += g[cnt[i]];
                /*
                    这里先把所有标本串的外层桶的值累加到tot上是为了防止包含的情况,如下例子
                    g(标本串): 1 1 2 3
                    f(当前区间) : 1 1 2
                    这实际上是一个不合法情况,不能统计进答案,但如果我们直接枚举在子串中出现的每个字母的出现次数就会出现这样的问题,所以先在前面加好
                    其实和处理完之后把剩下没有被枚举到的在标本串的内层桶中出现的元素累加上tot是一个效果,但这样需要多维护一个vis数组
                */
            for (register int i = 1; i <= 26; ++i) vis[cnt[i]] = false;
            for (register int i = 1; i <= 26; ++i)
                if (!vis[cnt1[i]]) {
                    vis[cnt1[i]] = true;
                    tot -= g[cnt1[i]];   //如果遇到在标本串和子串中同时出现的元素,先把这个减掉,防止影响后面统计答案
                    if (f[cnt1[i]] > g[cnt1[i]]) tot += (f[cnt1[i]] - g[cnt1[i]]);
                    else tot += (g[cnt1[i]] - f[cnt1[i]]);
                    //上面两行就是取绝对值
                }
            for (register int i = 1; i <= 26; ++i) vis[cnt1[i]] = false;
            ans = 0;
            if (!tot) ++ans;  //如果总和是0,增量答案
            //处理完第一个区间,开始向后挪动,下面i枚举的是右端点
            for (register int i = len + 1; i <= n; ++i) {
                add(s[i], 1);  //加入新右端点
                add(s[i - len], -1);  //删除原左端点
                if (!tot) ++ans;  //统计答案
            } printf("%d\n", ans);
            for (register int i = 1; i <= 26; ++i) --g[cnt[i]], --f[cnt1[i]];  //初始化,相当于前面统计g和f的过程反过来
        }
    }
    return 0;
}

T3

待补档,博主正在写

原文地址:https://www.cnblogs.com/kma093/p/11779732.html

时间: 2024-08-04 15:17:26

2019/11/1 CSP模拟的相关文章

2019/10/17 CSP模拟 总结

T1 补票 Ticket 没什么好说的,不讲了 T2 删数字 Number 很后悔的是其实考场上不仅想出了正解的方程,甚至连优化都想到了,却因为码力不足只打了\(O(n^2)\)暴力,甚至还因为细节挂成了\(40\ pts\) 以后还是应该多写一下码农题 规定一下,下面的\(j\)没有特殊说明,取值范围默认在\(1, i - 1\) 考虑什么情况是合法的,首先最后对答案有贡献的元素组成的序列一定是严格上升的,即\(a_i > a_j\) 然后考虑还有没有别的限制呢 放一个图 看到我们的对答案有贡

2019.11.11 题解报告

目录 2019.11.11 题解报告 答题情况: 各题目分析: 题目解析: 代码实现: 2019.11.11 题解报告 \[N^2\text{狂草1e5它不香嘛?}\] \[\text{By:Unluckierblock}\] 答题情况: 总成绩 : 169, 排名: 11 / 32 T1 : 0 T2 : 99 T3 : 70 各题目分析: 题目 1 : 预估成绩 : 60 实际成绩 : 0 考试用时 : 8 : 00 ~ 8 : 50 , 9 : 50 ~ 10 : 10 没有什么感觉 ,

【2019.11.5】

2019.11.5 开方 可以找到规律der 然后特判 开方五次最大为\(2^{32}-1\) 注意可能有前导零 要注意特殊数据\(0,1\)== 然后我没有注意到\(1\)... 行叭我是瘟猪 int main(){ //freopen("sqrt.in","r",stdin); //freopen("sqrt.out","w",stdout); for(;scanf("%s",s+1)!=EOF;){

11.7 CSP赛前集训小结

11.7 CSP赛前集训小结 今天是单赛日 1.上午的订正:没啥好说的 昨天的T1,稍微写下思路,维护高度差然后直接分情况讨论会很让人自闭,再加上符号+-和变化量的不同,会爆炸,那么不妨将之间的变化先减去,算上答案后再再加回来即可,正难则反的道理. #include<stdio.h> #include<bits/stdc++.h> #define maxn 200005 using namespace std; long long Ht[maxn]; long long n,q,s

2019.11.30训练赛总结

2019.11.30训练赛总结 Codeforces Round #499 (Div. 2) 总的来说是一场很不愉快的比赛.漏洞百出. 对于A题,其实没有什么技术含量,只是写的时候忘记了边界的情况,导致出现错误. B题,一定程度上考验了思维,既然从正面做不行,那么我可以反着来,既然求不可以正向求出答案,那我可以把答案枚举带进去看是否符合条件啊,如果数据范围再大点的话还可以二分. D题,也反应出了自己的一个漏洞,就是懒得一步一步去推数据,代数据进去.当出现bug的时候,最直接,最有效的办法,就是把

EOJ Monthly 2019.11 E. 数学题(莫比乌斯反演+杜教筛+拉格朗日插值)

传送门 题意: 统计\(k\)元组个数\((a_1,a_2,\cdots,a_n),1\leq a_i\leq n\)使得\(gcd(a_1,a_2,\cdots,a_k,n)=1\). 定义\(f(n,k)\)为满足要求的\(k\)元组个数,现在要求出\(\sum_{i=1}^n f(i,k),1\leq n\leq 10^9,1\leq k\leq 1000\). 思路: 首先来化简一下式子,题目要求的就是: \[ \begin{aligned} &\sum_{i=1}^n\sum_{j=1

@CSP模拟2019.10.16 - [email&#160;protected] 垃圾分类

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 为了保护环境,p6pou建设了一个垃圾分类器. 垃圾分类器是一个树形结构,由 n 个垃圾桶和 n-1 条双向传送带组成. 垃圾处理器的编号为 1, 2, ..., n,每条传送带都可以花 1 秒钟将垃圾从一个垃圾桶输送到另一个垃圾桶. 垃圾投放点是编号为 r 的垃圾桶,垃圾总是投放在这

2019/3/11 省选模拟总结

题面/题解:https://files.cnblogs.com/files/Harry-bh/problemset%281%29.rar T1.树上四次求和 主要考察了,对计数问题的分析,动态点分治的应用. 考试的时候因为边的数组开小了所以导致只得了暴力的分,以后一定要留意. 掌握程度 动态点分治:????? 计数问题:?? (推的时候很不熟练,差点没想到) T2.Cubelia 这是一道看起来比较简单的题,这也就导致了我花了大量时间来做这道题(结果考完后发现这道题最难?) 所以这体现了我考试经

2019.11.4模拟赛

T1 奇因数之和 定义\(F(n)\)为\(n\)的最大奇因数,例如\(F(1) = 1\),\(F(6) = 3\),\(F(12) = 3\). 输入\(m\),求\(\sum\limits_{i = 1}^{m}F(i)\). \(m \leq 10^{100}\) 分析一下我们发现,如果\(i\)是偶数,\(F(i) = F(i / 2)\),这样的话就可以分治求了. 这题难度在高精,谁考场上写大高精啊 r = open('sigma.in','r') w = open('sigma.o