A. Strong Password
解析:因为要让最终生成的密码最大,所以我们尝试是否在字符串中能找到两个相邻相同的字符,用一个不同的字符给它们隔开,如果没有找到,那就可以随便插入,但是要保证不能插入与前后相同的字符,所以我们可以在末尾插入一个与末尾字符不同的字符。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
void solved() {
string s;
cin>>s;
string s1;
int flag=0;
for (int i=0; i<s.size()-1; i++) {
s1+=s[i];
if (s[i]==s[i+1]&&!flag) {
if ('a'==s[i]) {
s1+='b';
} else {
s1+='a';
}
flag=1;
}
}
s1+=s[s.size()-1];
if (flag) {
cout<<s1<<"\n";
} else {
if ('a'==s1[s1.size()-1]) {
s1+='b';
} else {
s1+='a';
}
cout<<s1<<"\n";
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t=1;
cin>>t;
while (t--) {
solved();
}
}
B. Make Three Regions
解析:赛前没找到草稿本,正常比赛都是自己空想,这道题没画图当时直接写成史了。因为题目说给出的网格中最多只有一个相连区域,那么我们需要做到阻塞一个格子多出两个区域。我们画图可以发现只有像下面这种图才能做到
把黄色的区域堵住后,这时就会形成三个区域。
两排都可能出现这种情况,但是这时候要判断被点黄区域对面得是’x.x’这种,并且黄点区域左右不能出现‘x’,因为如果对面符合’x.x’,黄色区域左右如果有’x’,那就直接封闭了,不满足要求,还有就是第一个和最后一个不能点。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
string s[3];
void solved() {
int n;
cin>>n;
for (int i=1; i<=2; i++) {
cin>>s[i];
s[i]='&'+s[i]+'&';
}
int ans=0;
for (int i=2; i<=n-1; i++) {
if (s[1][i]=='.'&&s[1][i-1]!='x'&&s[1][i+1]!='x'&&s[2][i]=='.') {
if (s[2][i-1]=='x'&&s[2][i+1]=='x') {
ans++;
}
}
if (s[2][i]=='.'&&s[2][i-1]!='x'&&s[2][i+1]!='x'&&s[1][i]=='.') {
if (s[1][i-1]=='x'&&s[1][i+1]=='x') {
ans++;
}
}
}
cout<<ans<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t=1;
cin>>t;
while (t--) {
solved();
}
}
C. Even Positions
解析:要让成本尽可能小,那么让)尽早出现。赛时我还没看见奇数位置都是’_‘,偶数位置都是’(‘或’)',当时看样例硬写代码过的,我还在想我为什么没有被hack呢。有了这个条件就好做了,我们建一个栈将(放进去,遇到__如果栈是空的,则__为左扩号,遇到)直接从栈里取一个出来就行。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
void solved() {
int n;
cin>>n;
string s;
cin>>s;
stack<int>stk;
int ans=0;
for (int i=0; i<s.size(); i++) {
if (s[i]=='(') {
stk.push(i);
} else if (s[i]=='_') {
if (!stk.empty()) {
int pos=stk.top();
stk.pop();
ans+=(i-pos);
} else {
stk.push(i);
}
} else {
int pos=stk.top();
stk.pop();
ans+=(i-pos);
}
}
cout<<ans<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t=1;
cin>>t;
while (t--) {
solved();
}
}
D. Maximize the Root
解析:这道题有两种方法,一个是二分答案,二分根结点1的值,这里把题中的操作称为借,从上往下传需要借的点数,如果将需要借的传到最后,还不满足那么这个答案就不符合(注意二分的时候向下传小心溢出,因为每次是*2增长)。
另一个是树形DP,由于有一层假如需要借,那么它的子树都需要减一,所以我们需要从下往上平均一下,但是这个平均只能对于这个结点的子结点大,本身小,因为可以向下借,将本身变大。如果本身大于平均就不需要平均了,还得更新可以借的,因为要保证父结点要的时候大家都能接出去。
二分:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
int a[N];
vector<int>e[N];
int n;
int flag;
int dfs(int x,int fa,int mid) {
int ans=0;
if (mid>1e9) {//不加会溢出
flag=0;
return 0;
}
if (x==1) {
ans=mid;
} else {
if (a[x]-mid<0) {
ans=(mid-a[x])+mid;
} else {
ans=mid;
}
}
int xx=1;
for (auto v:e[x]) {
if (v==fa) {
continue;
}
xx=0;
dfs(v,x,ans);
}
if (xx==1) {
if (a[x]-mid<0) {
flag=0;
return 0;
}
}
return 0;
}
int check(int x) {
flag=1;
dfs(1,-1,x-a[1]);
if (flag!=0) {
return 1;
}
return 0;
}
void solved() {
cin>>n;
for (int i=1; i<=n; i++) {
e[i].clear();
cin>>a[i];
}
for (int i=2; i<=n; i++) {
int x;
cin>>x;
e[x].push_back(i);
}
int l=a[1]-1,r=3e9;
while (l+1<r) {
int mid=(l+r)/2;
if (check(mid)) {
l=mid;
} else {
r=mid;
}
}
cout<<l<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t=1;
cin>>t;
while (t--) {
solved();
}
}
树形DP
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e6+10;
const int mod=998244353;
int a[N];
vector<int>e[N];
int n;
int flag;
int dp[N];
int dfs(int x,int fa) {
int t=1e18;
for (auto v:e[x]) {
if (v==fa) {
continue;
}
t=min(t,dfs(v,x));
}
if (x==1){
return t;
}
if (t>=1e18){
return dp[x];
}
if (t>dp[x]) {
dp[x]=(t+dp[x])/2;
}
t=min(dp[x],t);
return t;
}
void solved() {
cin>>n;
for (int i=1; i<=n; i++) {
e[i].clear();
cin>>dp[i];
}
for (int i=2; i<=n; i++) {
int x;
cin>>x;
e[x].push_back(i);
}
int ans=dp[1]+dfs(1,-1);
cout<<ans<<"\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t=1;
cin>>t;
while (t--) {
solved();
}
}
E. Level Up
解析:
我们可以发现对于每个怪物有一个不能挑战k的阈值,当大于这个阈值都会与怪物战斗。
于是我们就可以二分找到每一个怪物的阈值,check里面去找能战斗的数量。
但正常从左到右去找的话会超时。
所以我们需要想一个方法去优化,相当于很快速的找到小于等于k的个数。
于是我们可以用到权值线段树,因为权值线段树的权值是按顺序的,我们正好用来存每一个k的数量。
查找的时候只需要加上当前每个k对应的下标数。
最后将二分的这个答案存到b[i]中,后面查询判断与b[i]的关系即可。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=998244353;
#define lson now<<1
#define rson now<<1 | 1
int a[N];
int tree[4*N];
int b[N];
int ans=0;
void insert(int now,int l,int r,int x,int op){
if (l==r){
tree[now]+=op;
return;
}
int mid=(l+r)/2;
if (x<=mid){
insert(lson,l,mid,x,op);
}else{
insert(rson,mid+1,r,x,op);
}
tree[now]=tree[lson]+tree[rson];
}
void querry(int now,int l,int r,int x){
if (l==r){
ans+=tree[now];
return;
}
int mid=(l+r)/2;
if (x<=mid){
querry(lson,l,mid,x);
}else{
ans+=tree[lson];
querry(rson,mid+1,r,x);
}
}
void solved() {
int n,q;
cin>>n>>q;
for (int i=1;i<=n;i++){
cin>>a[i];
}
//build(1,1,200000);
for (int i=1;i<=n;i++){
int l=0,r=200001;
while (l+1<r){
int mid=(l+r)/2;//二分最小能与第i个怪物战斗的k
ans=0;
querry(1,1,200000,mid);
if (ans/mid+1<=a[i]){
r=mid;
}else{
l=mid;
}
}
b[i]=r;
insert(1,1,200000,r,1);
}
while (q--){
int i,x;
cin>>i>>x;
if (b[i]<=x){
cout<<"YES\n";
}else{
cout<<"NO\n";
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t=1;
//cin>>t;
while (t--) {
solved();
}
}