#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n,A[N],m;
int main(){
int T;
cin>>T;
while(T--){
cin>>n>>m;
if(n==0){
cout<<"1\n";
}
else {
cout<<n+2*m+1<<endl;
}
}
return 0;
}
你收到一包含有n种类的糖果,你每次不能连续两次吃相同种类的糖果并且只能吃数量最多的糖果,问你能不能把所有糖吃完
考虑不能吃完的情况,那必定是有最大数量的糖果 - 较小数量的糖果 > 1,那样必须连续吃两次最大数量的糖果。
那么问题转化为,是否存在存在某个糖果能够减少到跟当前糖果数量一样 ,显然,如果说最大数量的两个糖果满足相差小于1,那么他们就可以减少到任意数量,答案就为YES,否则为NO
#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n,A[N],m;
int main(){
int T;
cin>>T;
while(T--){
cin>>n;
ll sum = 0;
for(int i = 0;i<n;i++){
cin>>A[i];
sum += A[i];
}
sort(A,A+n);
int ok= 1;
if(A[n-1]-A[n-2]>1) ok = 0;
if(ok){
cout<<"YES\n";
}
else cout<<"NO\n";
}
return 0;
}
定义一个字符串是合法的满足 1. 长度为偶数 2. 所有奇数位的字符都等于下一个字符
例如“aabb”,“ttllqq”是合法的,给你你个字符串,问你最少删除多少个字串能令其变成合法字符串
贪心来想 , 先遇到哪两个相同的字符就保留这两个字符,删除这两个字符其余部分,例如s = ababaac,先遇到的是aa,那么aa中间的b就舍弃,再到s[3] = b,找下一个b 的过程中发现已经有两个a匹配了,那就删去上一个匹配位置到这次匹配位置之间的字符,即b,最后别忘了处理一下末尾字符是否匹配了
定义一个数组A标记字符是否出现过,变量 l 记录上次匹配成功的下标,如果 在遍历时 字符已经出现一次,那么立即匹配,删除 l 到当前下标的字符,再重新让A数组的元素设为0,更新 l 的位置,最后如果l的位置不是最后一位,那么也要删去l到最后一位
#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll n,m;
bool A[200];
int main(){
int T;
cin>>T;
while(T--){
string s;
cin>>s;
memset(A,0,sizeof(A));
int n = s.length();
int l = 0;
int ans = 0;
//谁先出现就匹配谁
for(int i = 0;i<n;i++){
if(A[s[i]]) {
ans += i-l-1;
l = i+1;
for(int i = 'a';i<='z';i++) A[i] = 0;
}
else {
A[s[i]] = 1;
}
//M[s[i]] = i;
}
if(l!=n) ans += n-l;
cout<<ans<<endl;
}
return 0;
}
//acb
给你一个元素大小绝对值小于等于2的数组,你可以从头和从尾部删去若干个数,问你怎么删去原色能使剩下元素中乘积最大,如果数组为空,乘积为 1
#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
vector<int> rc;
vector<int> sign;
vector<int> val;
int A[N];
int main(){
int T;
cin>>T;
// 首先切割0位置,预处理出区域内负数的个数
while(T--){
int s = 0; // 2 的数量 ,直接乘放不下
int si = 1; // 符号
int n;
cin>>n;
rc.clear();
sign.clear();
val.clear();
memset(A,0,sizeof(A));
int sum = 0;
for(int i = 1;i<=n;i++){
cin>>A[i];
if(abs(A[i])>1) sum = 1;
if(A[i]==0){
rc.push_back(i),sign.push_back(si);
val.push_back(s);
s = 0,si = 1;
}
else {
if(A[i]==2||A[i]==-2) s++;
if(A[i]<0) si *= -1;
}
}
if(sum==0) {
cout<<n<<" "<<0<<endl;
continue;
}
sign.push_back(si);
rc.push_back(n+1);
val.push_back(s);
int l = 1;
ll ans_l = n+1,ans_r = n ,maxx = 0;
for(int i = 0;i<rc.size();i++){
int r = rc[i]-1;
if(l<=r){
int s = val[i];
int si = sign[i];
if(si>0&&s>=maxx) ans_l = l,ans_r = r,maxx = s;
else if(si<0){
ll mul = s,mul2 = s;
int tl,tr;
for(tl = l;tl<=r&&A[tl-1]>=0;tl++){
if(A[tl]==2||A[tl]==-2) mul--;
}
for(tr = r;tr>=l&&A[tr+1]>=0;tr--){
if(A[tr]==2||A[tr]==-2) mul2--;
}
if(mul>=maxx) ans_l = tl,ans_r = r,maxx= mul;
if(mul2>=maxx) ans_l = l,ans_r = tr,maxx= mul2;
}
}
l = rc[i]+1;
}
//答案
cout<<ans_l-1<<" "<<n-ans_r<<endl;
}
return 0;
}
// -2 -1 0 1 2
// 2 -1 1 2 2
给一个二维数组,可以上下左右滑动,类似成环,有一种操作可以让某一个位置取反,问最少需要多少次取反能够使矩阵变成单位矩阵(即除对角线外都是0)
#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
int G[5100][5010];
int col[2010],row[2010];
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
int cnt = 0;
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
char ch;
cin>>ch;
if(ch=='1')cnt++,G[i][j] =G[i+n][j]=G[i][j+n]=G[i+n][j+n] = 1;
else G[i][j] =G[i+n][j]=G[i][j+n]=G[i+n][j+n] = 0;
//形成环
}
}
int ans = INT_MAX;
for(int i = 1;i<=n;i++){
int cnt1 = 0;
int cnt0 = 0;
for(int k = 0;i+k<=2*n&&1+k<=2*n;k++){ //遍历斜对角线
if(G[i+k][1+k]==1) cnt1++;
else cnt0++;
if(k>=n-1){
ans = min(cnt-cnt1+cnt0,ans);
if(G[i+k-n+1][1+k-n+1]==1) cnt1--;
else cnt0++;
}
}
}
for(int j = 1;j<=n;j++){
int cnt1 = 0;
int cnt0 = 0;
for(int k = 0;1+k<=2*n&&j+k<=2*n;k++){ //遍历斜对角线
if(G[1+k][j+k]==1) cnt1++;
else cnt0++;
if(k>=n-1){
ans = min(cnt-cnt1+cnt0,ans);
if(G[1+k-n+1][j+k-n+1]==1) cnt1--;
else cnt0++;
}}
}
cout<<ans<<endl;
}
return 0;
}
F1和F2题意一样,区别为F1的n为1≤n≤3000,F2为1≤n≤2e5,给你只含“+”,“-”的字符串,你可以把相邻两个“-”转化为“+” , 问有多少个子字符串可以通过这种操作使“+”数量等于“-”,例如“±”,“±–”,都是合法的
前缀和sum处理出每个下标”-“与”+“的差
暴力枚举子字符串,当sum[j] >= sum[i]同时(sum[j]-sum[i])%3 == 0, 这个字符串就可以是合法的
#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
int sum[N];
char s[N];
int main(){
int T;
cin>>T;
while(T--){
int n;
memset(sum,0,sizeof(sum));
cin>>n>>(s+1);
for(int i = 1;i<=n;i++){
sum[i] = sum[i-1]+(s[i]=='+');
}
int ans = 0;
for(int i = 1;i<=n;i++){
for(int j = i+1;j<=n;j++){
int puls = sum[j]-sum[i-1];
int minus = j-i+1 - puls;
if(minus>=puls&&(minus-puls)%3==0){
// cout<
ans++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
与F1一样,只不过n是1e5
显然不能暴力枚举子字符串 , 我们来看关键的判断条件s[ j ] >= s[ i ] 和 (sum[j]-sum[i])%3 == 0, 其中后者可以转化成 sum[i] ==sum[j] (在模3的意义下)
那么做法就是: 枚举每个子字符串的终点,统计出前面sum[j] >= sum[ i ] 同时 sum[j]%3 ==sum[i] %3的数量就好,那么对于每个点 i 我们可以用三个树状数组分别标记一下sum[i] ,要查询贡献度时在相应的树状数组中用前缀和相加就可以得到符合要求起点的数量。
例如{1,2,3,4} 对于sum[0] = 1, 模3也为1 ,那么在第二个树状数组中(第一个是记录模数为0的)记录一下sum[0]已经出现过,在遍历到sum[3] = 4,模数也为1,查询第二个树状数组中1~sum[4]的前缀和,就是这个点的贡献值;
但是由于sum数组会有负数,所以最后加上一个偏移量,让所有数大于0,不影响sum[j]-sum[i])%3 == 0的判断
#include
using namespace std;
const int N = 1e6+10;
typedef long long ll;
int n;
int tree[N][3];
void add(int ix,int x,int m){
for(int i = ix;i<=N;i+= (i&-i)){
tree[i][m] += x;
}
}
ll query(int ix,int m){
ll ans = 0;
for(int i = ix;i>=1;i-=(i&-i)){
ans += tree[i][m];
}
return ans;
}
char s[N];
int sum[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
cin>>n;
//string s;
cin>>(s+1);
int minn = 0;
sum[0] = 0;
for(int i = 1;i<=n;i++){
sum[i] = sum[i-1]+(s[i]=='+'?-1:1);
minn = min(minn,sum[i]);
}
for(int i = 0;i<=n-minn+10;i++) tree[i][0]=tree[i][1]=tree[i][2] = 0;
ll res = 0;
for(int i = 0;i<=n;i++) sum[i] -= minn-1;
for(int i = 0;i<=n;i++){
int need = sum[i]%3;
res += query(sum[i],need);
//cout<
add(sum[i],1,need);
}
cout<<res<<'\n';
}
return 0;
}