A-小红劈字符串
题目描述
小红拿到了一个字符串,她准备把这个字符串劈成两部分,使得第一部分的长度恰好是第二部分的两倍。你能帮帮她吗?
输入格式
一个仅由小写字母组成的字符串,长度不超过
输出格式
无解输出-1
有解则输出两个劈好了的字符串
题解
先看字符串长度是否为3的倍数,然后先输出前2/3的长度,在输出后面的部分就好了
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
const int N=2e5+5;
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
string s;
cin>>s;
int len=s.length();
if(len%3)
{
cout<<-1;
return 0;
}
for(int i=0;i<len/3*2;i++)
cout<<s[i];
cout<<' ';
for(int i=len/3*2;i<len;i++)
cout<<s[i];
}
B-小红的数字分裂
题目描述
小红有一个数组,她每次可以选择数组的一个元素 x ,将这个元素分成两个元素 a 和 b ,使得
x=a+b
请问小红最少需要操作多少次才可以使得数组的所有元素都相等。
输入格式
第一行一个整数n,其中,表示数组长度
第二行输入n个整数表示数组
输出格式
输出一个整数表示答案。
题解
要把所有的数都分解成相同的数,那么最后的数应该是数组中所有数的公因数
又因为所有要求操作次数最小,所以应该是去找最大公因数,假设该最大公因数是g
那么对答案的贡献应该等于
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> PII;
ll a[N];
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n;
cin>>n;
ll g=0;
ll ans=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
g=__gcd(g,a[i]);
}
for(int i=1;i<=n;i++)
{
ans+=(a[i]/g)-1;
}
cout<<ans;
}
C-小红的字符串同构
题目描述
小红定义两个字符串同构,当且仅当对于 i∈[1,n ], b[i]−a[i] i∈[1,n], b[i]-a[i] i∈[1,n],b[i]−a[i]是定值。例如,"bacd"和"edfg"是同构的。
现在小红拿到了一个长度为n的字符串a,她想知道,有多少长度为n的字符b同时满足以下两个条件:
1.b的每一位都和a不同。
2.b和a不同构。
输入格式
一个仅由小写字母组成的,长度不超过的字符串a
输出格式
一个整数表示答案,对1e9+7取模
题解1
首先来理解一下题目描述的 “同构” ,也就是说两个字符串长度相等,并且每个字符之间的 “位移”是相等的(注意,这里的位移是有正负的),举个例子:cd 与 ab 同构,但 cd 不与 af 同构,因为'c' - 'a'= - 2,'d' - 'f' = 2
这里提供一个O(26*N)的做法:
假设字符串长度为len
首先,枚举合法的字符串b的第一位是哪个字母,这样我们能确定位移d(d ! = 0)
然后,遍历2-len位
我们首先判断 a[i]+d是否还是小写字母
若不是小写字母,则一定不会构成同构,后面每一位都能选25个字母,遍历终止
计算答案
ans=(ans+POW(25,1ll*(s.size()-j)))%P;
若还是小写字母,则1-i位可能构成同构
此时我们再分类讨论:
1.若不选该字母,则不可能构成同构,当前位可选24个字母,后面每一位可选25个字母
计算答案
ans=(ans+(24*POW(25,1ll*(s.size()-j-1)))%P)%P;
2.选择该字母,则1-i位是同构的,观察下一位
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
const int N=2e5+5;
typedef long long ll;
typedef pair<int,int> PII;
int e[N],ne[N],h[N],idx;
ll finv[N],fac[N];
ll P=1e9+7;
ll POW(ll x,int k=P-2,ll rs=1){while(k){if(k&1)rs=rs*x%P;x=x*x%P;k>>=1;}return rs;}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
string s;
cin>>s;
ll ans=0;
//枚举第一位可选哪个字母
for(int i='a';i<='z';i++)
{
//d!=0
if(s[0]==i)
{
continue;
}
int d=i-s[0];
for(int j=1;j<s.size();j++)
{
int now=s[j]+d;
//若前j位可以构成同构,则当前位填其余24个字母之后,(0到j-1位都是同构的)后面每一位都能填25个字母,计算一次答案
//若当前位填now,(0-j位都是同构的),观察下一位,继续循环
if(now>='a' && now<='z')
{
ans=(ans+(24*POW(25,1ll*(s.size()-j-1)))%P)%P;
}
//如果此时一定不能构成同构,则j位到最后一位都能填25个字母,计算答案,循环终止
else
{
ans=(ans+POW(25,1ll*(s.size()-j)))%P;
break;
}
}
}
cout<<ans;
}
这也是愚钝的博主最开始想出来的做法,下面来介绍官方的O(N)的做法
题解2
首先计算满足 条件1 的字符串的个数,答案为
而同构的字符串数量也能直接计算出来,答案为26-(最大的字符-最小的字符+1)
如何来理解这个同构的字符串数量的计算?
举个例子,如图,假设字符串a中最小的字符是 'c',最大的是 'w',因为a字符串中的每个字符都要进行 “移动”,且不能超过小写字母的范围,假设 ' w ' 变成 ' t ',那么' c '变成' a ' 之后还要再往前移动,所以超过了小写字母范围,对 'w',来说同理,所以同构字符串的数量等于图中绿线可移动的范围
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod=1e9+7;
signed main(){
string s;
cin>>s;
int ma=0,mi=1e9;
ll res=1;
for(auto i:s){
ma=max(ma,(int)(i-'a'));
mi=min(mi,(int)(i-'a'));
res=res*25%mod;
}
cout<<res-(26-(ma-mi+1));
}
D-小红的树上赋值方案
题目描述
小红拿到了一棵有根树,其中有一些节点被染成了红色。树的根节点是 1 号节点。
小红希望你给每个节点的权值赋值为 1 或者 2,需要满足每个红色节点的子树节点权值之和为 3 的倍数。
请你帮小红求出赋值的合法方案数。由于答案可能过大,请对1e9+7取模
输入格式
第一行输入一个正整数n,代表节点的数量。
第二行输入一个长度为n的字符串,第i个字符为'R'代表i号节点被染成红色,为'W'代表未被染色。
第三行输入n−1个正整数ai,第i个正整数代表i+1号节点的父亲编号。
输出格式
一个整数代表答案
题解
官方题解是树形dp,很好理解,这里给出博主赛时想到的做法
首先考虑,一颗根被染成了红色,且其他根没有被染色的大小为size的树的答案
那么这个树的所有节点权值和value满足
假设所有节点权值为1,value=size,为2,value=2*size
那么我们假设树中所有节点权值都为1,然后枚举权值从1变成2的节点个数,若总和为3的倍数,我们用组合数计算一次答案
举个例子:
这里的size=5,那当权值为2的节点个数是1,4的时候可以满足题意
那么这颗 “红根树”的答案就是
这是根被染成红色时的答案
那么对于原来的题意应该怎么做呢?
其实我们可以发现,如果一颗根是红色的树有红根子树,那么我们就不需要考虑它了,因为这颗树的方案已经被计算出来了,那就把这个红根子树的size当作0就好了,然后答案还要累乘上当前红根树的贡献
这里的答案就是
最后来考虑一些特殊情况:
1.如果树上没有红色的节点,那么每个节点都能填1或2,答案就是
2.如果一颗根是红色的树的size=1,那么最后的答案应该是0
想清楚之后我们就能写代码了
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
const int N=1e5+5,M=2*N;
typedef long long ll;
typedef pair<int,int> PII;
int e[M],ne[M],h[N],idx;
ll finv[N],fac[N];
ll P=1e9+7;
ll POW(ll x,int k=P-2,ll rs=1){while(k){if(k&1)rs=rs*x%P;x=x*x%P;k>>=1;}return rs;}
void init()
{
finv[1]=finv[0]=fac[0]=1;
for(int i=1;i<N;++i)fac[i]=fac[i-1]*i%P;
finv[N-1]=POW(fac[N-1]);
for(int i=N-2;i;--i)finv[i]=finv[i+1]*(i+1)%P;
}
ll C(int a,int b)
{
if(b>a||a<0||b<0)return 0;
return fac[a]*finv[a-b]%P*finv[b]%P;
}
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
char color[N];
int sz[N];
ll ans=1;
void dfs1(int x,int fa)
{
sz[x]=1;
for(int i=h[x];~i;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
dfs1(j,x);
//不计算红色子树的size
if(color[j]=='R')
continue;
sz[x]+=sz[j];
}
}
void dfs2(int x,int fa)
{
if(color[x]=='R')
{
ll cal=0;
for(int i=0;i<=sz[x];i++)
{
if((sz[x]+i)%3==0)
{
cal=(cal+C(sz[x],i))%P;
}
}
// cout<<"cal="<<cal<<endl;
ans=(ans*cal)%P;
}
for(int i=h[x];~i;i=ne[i])
{
int j=e[i];
if(j==fa) continue;
dfs2(j,x);
}
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
//初始化求组合数的部分
init();
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>color[i];
if(color[i]=='R')
h[i]=-1;
}
int fa;
for(int i=2;i<=n;i++)
{
cin>>fa;
add(fa,i),add(i,fa);
}
//dfs1求出每颗红根树的size
dfs1(1,0);
dfs2(1,0);
/*
如果1号根节点没有被染色
那么答案还要乘上2的根节点size的幂次
*/
if(color[1]=='W')
{
ans=(ans*POW(2,sz[1]))%P;
}
cout<<ans;
}
E-小红的区间查询
题目描述
小红拿到了一个几何,初始为空集。小红可以进行下面两种操作:
+ l r :将区间(l,r)添加进集合
- l r : 将区间(l,r)从集合中删除
删除时保证一定能删除
问每次操作之后是否存在两个区间相交
输入格式
第一行输入一个正整数q,代表操作次数
接下来q行输出一个字符op与两个正整数l,r,代表操作
输出格式
存在输出"Yes",不存在输出"No"
题解
很容易想到直接用线段树维护区间加,每次操作后查询区间最大值是否>1即可
由于l,r范围很大,但是q的次数很小,所以我们可以先进行离散化处理
这里附上一份官方代码
#include <bits/stdc++.h>
using ll = long long;
template<class Info, class Tag, class Merge = std::plus<Info>>
class LazySegmentTree {
private:
int n;
const Merge merge{};
std::vector<Info> info; // data of segment tree, 1-index
std::vector<Tag> tag; // lazy tag of segment tree
/* [x, y) and val: Add val to each element in range of [x, y)
* p: The id of subtree, which is an index of vector 'info'.
* [l, r): The range of p.
*/
void innerPull(int p) {
info[p] = merge(info[p << 1], info[p << 1 | 1]);
}
void innerApply(int p, const Tag &v, int l, int r) {
// TODO: update the apply function
auto applyInfo = [&](Info &a, const Tag &b, int l, int r) {
a.min += b;
a.max += b;
};
auto applyTag = [&](Tag &a, const Tag &b, int l, int r) {
a += b;
};
applyInfo(info[p], v, l, r);
applyTag(tag[p], v, l, r);
}
void push(int p, int l, int r) {
if (tag[p] != Tag()) {
int m = (l + r) / 2;
innerApply(p << 1, tag[p], l, m);
innerApply(p << 1 | 1, tag[p], m, r);
tag[p] = Tag();
}
}
void update_(int p, int x, int y, const Tag &v, int l, int r) {
if (x <= l && r <= y) {
innerApply(p, v, l, r);
return;
}
int m = (l + r) / 2;
push(p, l, r);
if (x < m) update_(p << 1, x, y, v, l, m);
if (y > m) update_(p << 1 | 1, x, y, v, m, r);
innerPull(p);
}
/* Query the sum-up value of range [x, y). */
Info query_(int p, int x, int y, int l, int r) {
if (x <= l && r <= y) return info[p];
if (x >= r || y <= l) return Info();
int m = (l + r) / 2;
push(p, l, r);
return merge(query_(p << 1, x, y, l, m), query_(p << 1 | 1, x, y, m, r));
}
public:
LazySegmentTree(int n = 0) { init(n); }
LazySegmentTree(std::vector<Info> &init) : LazySegmentTree(init.size()) {
std::function<void(int, int, int)> build_ = [&](int p, int l, int r) {
if (r - l == 1) {
info[p] = init[l];
return;
}
int m = (l + r) / 2;
build_(p << 1, l, m);
build_(p << 1 | 1, m, r);
innerPull(p);
};
build_(1, 0, n);
}
void init(int n) {
this->n = n;
info.assign(4 << std::__lg(n), Info(0, 0));
// info.resize(4 << std::__lg(n));
tag.resize(4 << std::__lg(n));
}
/* Add val to each element in range of [x, y) */
void update(int x, int y, Tag v) {
update_(1, x, y, v, 0, n);
}
/* Query the sum-up value of range [x, y) */
Info query(int x, int y) {
return query_(1, x, y, 0, n);
}
};
struct MinMax {
static constexpr int inf = 1e9;
int min, max;
MinMax(int min = inf, int max = -inf) : min(min), max(max) {}
MinMax operator+(const MinMax &rhs) const {
return MinMax(std::min(min, rhs.min), std::max(max, rhs.max));
}
MinMax &operator+=(const MinMax &rhs) {
return *this = *this + rhs;
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int m;
std::cin >> m;
std::set<std::pair<int, int>> st;
std::vector<std::tuple<char, int, int>> ops;
while (m--) {
char op;
int l, r;
std::cin >> op >> l >> r;
ops.emplace_back(op, l, r);
if (op == '+') {
assert(st.find(std::pair(l, r)) == st.end());
st.emplace(l, r);
} else {
assert(st.find(std::pair(l, r)) != st.end());
st.erase(std::pair(l, r));
}
}
std::vector<int> a;
for (auto [op, l, r] : ops) {
a.push_back(l);
a.push_back(r);
}
std::sort(a.begin(), a.end());
a.erase(std::unique(a.begin(), a.end()), a.end());
for (auto &[op, l, r] : ops) {
l = std::lower_bound(a.begin(), a.end(), l) - a.begin();
r = std::lower_bound(a.begin(), a.end(), r) - a.begin();
}
LazySegmentTree<MinMax, int> seg(a.size());
for (auto [op, l, r] : ops) {
if (op == '+') {
seg.update(l, r, 1);
} else {
seg.update(l, r, -1);
}
if (auto [min, max] = seg.query(0, a.size()); min < 0 || max > 1) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
}
return 0;
}