リフレクションEDITORIAL
私は怠け者と遅かったし、最後の質問は、実際には、これら2つの点が難しいことではありません。図のデイジーチェーンの場合を書いていないので、ポイントはやはり+、ああ終わっGETに参加しなければなりません。。
のみが(10pts \)\が、長さ、ああ、追加登るのに十分な\(10ptsを\)今日のフォルクスワーゲン分割に\(210ptsが\)よく見るの文脈で重要である
別の\(T2 \)私は最後の発見しました偽判断する置くが、幸い何の問題を移動しないでレンジデータ内で実行するようだが、それでもカードに良いカードが、このカードは書き込みや公式に注意を払うために幸運次回だけではありません必ずしもカードゲーム
T1
\(SB \)表題
する\(1件のB == \) 、全ての総数を判断する\(GCDを\)分割できない\(\)
する(B == 2 \)を\、最小開口部の総数が決定牙は、より厳密に大きいではありません\(\)
\(SQRT()\)の戻り値は私の命を救うために、浮動小数点数で、私はほとんどそれをしたい、破裂することが分かっ\(長い\長い\)
T2
考え
バレルのバケットセット
(第1層)が表示され、各文字列のサンプルの数を記録し、各文字の発生の数を提供(\ cnt_i)\、第2層\(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
ファイルを完了するために、ブロガーが書いています