C - 1111gal password (DP)
题意:找到长度为n的且满足相邻位之差的绝对值不超过1的方案数
思路:f[i][j]表示前i个位且第i位填j满足要求的方案数, 第i位填j,那么第i-1位肯定就只能填(j-1, j, j + 1)这三个数,因此可以得到转移方程:
f[i][j] = f[i-1][j-1] + f[i-1][j] + f[i-1][j+1];
时间复杂度是O(10*N)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1e6 + 10, mod = 998244353;
ll f[N][12];
inline int read()
{
int x = 0, f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= ch=='-', ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return f ? -x : x;
}
void solve()
{
int n = read();
for (int i = 1; i <= 9; i ++ ) f[1][i] = 1;
for (int i = 2; i <= n; i ++ )
{
for (int j = 1; j <= 9; j ++ )
f[i][j] = (f[i-1][j-1] + f[i-1][j] + f[i-1][j+1]) % mod;
}
ll ans = 0;
for (int i = 1; i <= 9; i ++ ) ans = (ans + f[n][i]) % mod;
printf("%lld\n", ans);
}
int main()
{
int t = 1;
while (t -- ) solve();
return 0;
}
D - ABC Transform
题意
思路:我们可以发现对于x->yz的变化,第一个字符是x向右偏移一位,第二个字符是x向右偏移两位(如果大于C就绕回A)。我们还可以发现
对于当前位置i的字符p它经过一次替换后的两个字符的位置一定是2i-1和2i这两个位置。
因此对于进行了ti次变化后的S串的第k个位置的字符p,它一定是由进行的ti-1次变化后的S串的第(k+1)/2(相当于向上取整)个位置的字符q经过变化得来的,如果k是奇数,那么p相当于就是字符q偏移一位得来的,如果是偶数则是偏移了两位得来的。
因此对于一次询问(ti, ki)如果我们能够知道ti-1次变化的第(ki+1)/2字符那我们就能推出这个字符,因此题目又转化成求(ti-1, (ki+1)/2),这又成为了原问题,我们可以照样往上面找,由于ki最大是1e18,每次向上找ki相当于都要除2,因此最多向上找60次ki就会变成1,对于这一时刻ti的第一个字符我们可以根据此时是经过多少次变化就可以轻松得到。还有一种就是可能ti<60,那么当我们向上找的那个时刻的ti变成了0,我们可以直接返回原字符串的第k位,这一过程可以用递归来实现,时间复杂度O(Qlogk)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1e5 + 10;
char s[N];
inline int read()
{
int x = 0, f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= ch=='-', ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return f ? -x : x;
}
char dfs(ll k, ll t)
{
if (!t) return s[k];
if (k == 1) // 第一个字符可以很容易由原串的第一个字符得到
{
int x = (s[1] - 'A' + t%3) % 3;
return 'A' + x;
}
char ch = dfs((k + 1) / 2, t - 1); // 获取t-1次变化后的第(k+1)/2个字符
if (k & 1) // 偏移量是1
{
int x = (ch - 'A' + 1) % 3;
return 'A' + x;
}
else // 偏移量是2
{
int x = (ch - 'A' + 2) % 3;
return 'A' + x;
}
}
void solve()
{
scanf("%s", s + 1);
int m = read();
while (m -- )
{
ll t, k;
scanf("%lld%lld", &t, &k);
printf("%c\n", dfs(k, t));
}
}
int main()
{
int t = 1;
while (t -- ) solve();
return 0;
}
E - (∀x∀)
题意:给定一个S串,询问有多少种字符串X满足以下条件
1.仅由大写字母组成且长度与S串相同
2.X是一个回文字符串
3.X的字典序小于等于S的字典序
思路:由于字符串是回文串,因此构造的时候后面那半部分就可以不用管其会随着前面的确定而确定,因此在这里我们只考虑前半部分的构造方案。
我们可以分成前半部分和S串相同和不相同的情况来考虑
对于构造的X前半部分和S相同的情况:
我们可以将X的前半部分翻转一下然后拼接到后面,然后在比较一下是否满足要求,满足要求的话其对答案的贡献是1,否则是0
对于构造的X前半部分和S不同的情况:
那么X必然存在一个位置i的字符严格小于S[i], 因此我们可以枚举这个i,然后对于这些方案数相加即可。假设对于当前构造的字符串的1~i-1位都是相同的且第i位字符是严格小于S[i]的,那么后面的那些字符我可以任意填其方案数为26^(n-i) (n为前半部分的字符串长度),第i为严格小于S[i]的方案数由x=(S[i] - ‘A’)种,因此其对答案的贡献为x * (26 ^ (n-i))。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1e6 + 10, mod = 998244353;
char s[N];
inline int read()
{
int x = 0, f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= ch=='-', ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return f ? -x : x;
}
ll qmi(ll a, int b)
{
ll t = 1;
while (b)
{
if (b & 1) t = t * a % mod;
b >>= 1;
a = a * a % mod;
}
return t;
}
void solve()
{
int n = read();
scanf("%s", s + 1);
ll ans = 0;
for (int i = 1; i <= (n + 1) / 2; i ++ )
{
int x = s[i] - 'A';
ans = (ans + 1ll * x * qmi(26, (n + 1) / 2 - i)) % mod;
}
// cout << ans << endl;
bool flag = true;
for (int i = n / 2 + 1 + (n&1); i <= n; i ++ )
if (s[i] < s[n-i+1]) flag = false;
else if (s[i] > s[n-i+1]) break;
ans = (ans + flag) % mod;
printf("%lld\n", ans);
}
int main()
{
int t = read();
while (t -- ) solve();
return 0;
}
G - Range Pairing Query (莫队)
题意:给你编号分别为1~n的人,第i个人有一个颜色colori,每次询问求出[l, r]这个区间内最多可以组成多少对颜色相同的人
思路:纯纯莫队板子,就不细说了,学了莫队就能做。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 5e5 + 10, M = 1e6 + 10;
int g[N], pos[N];
int num[N];
struct node
{
int l, r, idx;
friend bool operator <(node a, node b)
{
if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
if (a.r != b.r) return a.r < b.r;
return a.idx < b.idx;
}
}qu[M];
ll res = 0;
ll ans[M];
inline int read()
{
int x = 0, f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= ch=='-', ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return f ? -x : x;
}
void del(int x)
{
num[g[x]] --;
res -= (num[g[x]] & 1);
}
void add(int x)
{
res += (num[g[x]] & 1);
num[g[x]] ++;
}
void solve()
{
int n = read();
int block = sqrt(n);
for (int i = 1; i <= n; i ++ ) g[i] = read(), pos[i] = i / block;
int m = read();
for (int i = 1; i <= m; i ++ )
{
int l = read(), r = read();
qu[i] = {l, r, i};
}
sort(qu + 1, qu + m + 1);
int l = 1, r = 0;
for (int i = 1; i <= m; i ++ )
{
while (l < qu[i].l) del(l++);
while (l > qu[i].l) add(--l);
while (r < qu[i].r) add(++r);
while (r > qu[i].r) del(r --);
ans[qu[i].idx] = res;
}
for (int i = 1; i <= m; i ++ )
printf("%lld\n", ans[i]);
}
int main()
{
int t = 1;
while (t -- ) solve();
return 0;
}
完结,如有错误还请指正,感谢!!!