A. Dr. TC
有n次翻转,从1到n,0->1,1->0,每次统计1的数量,设cnt1是字符串1的数量,n次就是n*cnt1,
但每个1都会被翻转一次减去一个cnt1,再统计cnt0,每个被翻转一次,答案就是(n-1)*cnt1+cnt0
#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
#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的最小值与最大值,即可求
#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是否合法
#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的不合法次数,抵消到有剩余,判断剩余的有没有对应剩余的可抵消
#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数量
#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;
}