A. Dr. TC

有n次翻转,从1到n,0->1,1->0,每次统计1的数量,设cnt1是字符串1的数量,n次就是n*cnt1,
但每个1都会被翻转一次减去一个cnt1,再统计cnt0,每个被翻转一次,答案就是(n-1)*cnt1+cnt0
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n;cin>>n;
string s;cin>>s;
int cnt1=0,cnt0=0;
for(int i=0;i<n;i++)
{
if(s[i]=='1')cnt1++;
else cnt0++;
}
cout<<(n-1)*cnt1+cnt0<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
B. St. Chroma

给一个排列,从1到n依次做mex操作,让x出现次数最多 ,要出现x,要先排0~x-1,再放x后面的数字,最后再放x
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n,x;cin>>n>>x;
vector<int>ans(n);
for(int i=0;i<x;i++)ans[i]=i;
for(int i=x;i<n-1;i++)ans[i]=i+1;
ans[n-1]=x==n?x-1:x;
for(int i=0;i<n;i++)cout<<ans[i]<<" ";
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
C. Cherry Bomb

给出a和b数组,当对应和都相等,就是互补数组,b中有缺失,求方案数
先找无解,那就是给出的对应和有多个 ,或凭借b的范围无法凑出对应和
有解的话就是1个,或b全是-1,有多个,找a的最小值与最大值,即可求
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n,k;cin>>n>>k;
ll maxx=-2,minn=1e18;
ll sum=-1;
vector<ll>a(n+1),b(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
maxx=max(maxx,a[i]);
minn=min(minn,a[i]);
}
bool tag=true;
for(int i=1;i<=n;i++)
{
cin>>b[i];
if(b[i]!=-1)
{
if(sum==-1)sum=a[i]+b[i];
else
{
if(a[i]+b[i]!=sum)tag=false;
}
}
}
if(!tag)
{
cout<<0<<endl;
}
else if(sum!=-1)
{
bool flag=true;
for(int i=1;i<=n&&flag;i++)
{
if(b[i]==-1)
{
if(a[i]+k<sum||a[i]>sum)flag=false;
}
}
if(flag)cout<<1<<endl;
else cout<<0<<endl;
}
else
{
ll up=minn+k;
if(up<maxx)cout<<0<<endl;
else cout<<up-maxx+1<<endl;
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
D. Flower Boy

在a中找出一个长为m的序列,让对应ai都大于bi,也可删去b中一个再找
不操作有解直接输出
操做的话,考虑枚举删去的b,对a做前缀和与后缀和,pre[i]表示前i个元素可匹配b中前多少个,suf[i]同理
枚举b的过程中,到i,表示要找i-1个先匹配,再找n-i个在后面匹配,二分pre数组,看suf是否合法
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n,m;cin>>n>>m;
vector<ll>a(n+1),b(m+1);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>b[i];
int pos=1;
for(int i=1;i<=n;i++)
{
if(pos!=m+1&&a[i]>=b[pos])pos++;
}
if(pos==m+1)
{
cout<<0<<endl;
return;
}
vector<int>pre(n+1,0),suf(n+3,0);
int t=1;
for(int i=1;i<=n;i++)
{
int k=0;
if(a[i]>=b[t])t++,k=1;
pre[i]=pre[i-1]+k;
}
//for(int i=1;i<=n;i++)cout<<pre[i]<<" ";
//cout<<endl;
t=m;
for(int i=n;i>=0;i--)
{
int k=0;
if(a[i]>=b[t])t--,k=1;
suf[i]=suf[i+1]+k;
}
ll ans=1e18;
for(int i=1;i<=m;i++)
{
int pos=lower_bound(pre.begin(),pre.end(),i-1)-pre.begin();
//cout<<pos<<endl;
if(pos>n)continue;
if(pre[pos]==i-1&&suf[pos+1]>=m-i)ans=min(ans,b[i]);
}
if(ans==1e18)cout<<-1<<endl;
else cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
/*
2
5 5
7 7 6 7 7
7 7 7 7 7
*/
E. Wolf

给出一个排列,有q次询问,询问l到r中能否二分到x,不可输出-1,可的话找最小操作数
可做的操作是对数组除x以外的数任意调换顺序,找调换顺序的最小个数
可用st数组记录每个元素的位置
#1.当mid<st[x],且p[mid]>x需要操作,将p[mid]换成小于x的
#2.当mid>st[x],且p[mid]<x需要操作,将p[mid]换成大于x的
注意到1与2,之间可以直接交换使其都合法
模拟二分过程,记录mid>x的次数,和mid>x并且合法次数
记录mid<x次数,和mid<x合法次数
于是就得到了mid<x与>x的不合法次数,抵消到有剩余,判断剩余的有没有对应剩余的可抵消
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n,q;scanf("%d%d",&n,&q);
vector<int>p(n+1),st(n+1);
for(int i=1;i<=n;i++)
{
cin>>p[i];
st[p[i]]=i;
}
vector<int>ans;
while(q--)
{
int l,r,x;scanf("%d%d%d",&l,&r,&x);
if(st[x]<l||st[x]>r)
{
ans.push_back(-1);
continue;
}
if(l==r)
{
if(p[l]==x)ans.push_back(0);
else ans.push_back(-1);
continue;
}
int L=0,R=0,LL=0,RR=0;
while(l<r)
{
int mid=(l+r)/2;
if(p[mid]==x)break;
if(mid<st[x])
{
L++;
if(p[mid]<x)LL++;
l=mid+1;
}
else
{
R++;
if(p[mid]>x)RR++;
r=mid-1;
}
//cout<<l<<endl;
}
//cout<<l<<" "<<r<<endl;
//cout<<cnt<<" "<<ok<<endl;
if(L>x-1||R>n-x)ans.push_back(-1);
else
{
L-=LL;
R-=RR;
if(L>=R)
{
ll tmp=L-R;
if(x-1>=tmp+LL+R)ans.push_back(2ll*R+2ll*(L-R));
else ans.push_back(-1);
}
else
{
ll tmp=R-L;
if(n-x>=tmp+RR+L)ans.push_back(2ll*L+2ll*(R-L));
else ans.push_back(-1);
}
}
}
for(auto y:ans)printf("%d ",y);
printf("\n");
}
int main()
{
int t = 1;scanf("%d",&t);
while (t--)
{
solve();
}
return 0;
}
/*
3
13 1
12 13 10 9 8 4 11 5 7 6 2 1 3
1 13 2
*/
F. Goblin

与a题共享题面,但问的不同,n次操作后,我们需要找出这n*n的方格中 最大连通0的数量
考虑dp
注意到每次操作的数形成了主对角线
对于每个字符i,它在aii处翻转,其余保持不变
我们一列一列的添加,发现出现了四种状态转移
0->1,0->0,1->0,1->1
并且在一列一列添加的过程中,场上0的联通块数量不会大于2
因为如果s[i]是0,中间有1,其余为0,将其隔开有两个连通块,如s[i+1]是0,它的上连通块和下联通块会继承s[i]的
如果s[i+1]是1,一个0隔开上列1和下列1,它会将上连通块截止,继承上一个的下连通块
设置状态dp[i][0]表示到第i列,上连通块0的数量
dp[i][1]表示到第i列,下连通块0数量


cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n;cin>>n;
string s;cin>>s;
s="#"+s;
vector<vector<ll>>dp(n+1,vector<ll>(2,0));
if(s[1]=='0')dp[1][1]=n-1;
else dp[1][1]=1;
ll ans=0;
for(int i=2;i<=n;i++)
{
if(s[i-1]=='0'&&s[i]=='1')
{
ans=max(ans,dp[i-1][0]);
dp[i][1]=dp[i-1][1]+1;
}
else if(s[i-1]=='0'&&s[i]=='0')
{
dp[i][0]=dp[i-1][0]+i-1;
dp[i][1]=dp[i-1][1]+n-i;
}
else if(s[i-1]=='1'&&s[i]=='0')
{
dp[i][0]=dp[i-1][1]+i-1;
dp[i][1]=n-i;
}
else
{
ans=max(ans,dp[i-1][1]);
dp[i][1]=1;
}
}
ans=max(ans,dp[n][0]);
ans=max(ans,dp[n][1]);
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}