A. Replacing Elements
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N];
int main()
{
ios::sync_with_stdio(false);
int T,n,d;
cin>>T;
while(T--)
{
cin>>n>>d;
bool flag=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>d)flag=1;
}
sort(a+1,a+n+1);
if(!flag)printf("YES\n");
else
{
if(a[1]+a[2]<=d)printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
B. String LCM
思路:找出两个字符串的最小重复子串u,并计算其重复次数分别为cnt1,cnt2,输出 l c m ( c n t 1 , c n t 2 ) lcm(cnt1,cnt2) lcm(cnt1,cnt2)个子串u即可。如果两个字符串的最小重复子串不同,则输出-1。
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int T,a[N];
string s1,s2;
int judge(string a,string b)
// a为b的子串,b由a重复构成(若不符合条件返回-1),返回a出现次数
{
int n1=a.length();
int n2=b.length();
if(n2%n1!=0)return -1;
for(int i=0;i<n2;i+=n1)
{
string t=b.substr(i,n1);
if(t!=a)return -1;
}
return n2/n1;
}
int main()
{
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>s1>>s2;
int n1=s1.length();
string u; // s1的最小单元
int cnt1;
for(int i=1;i<=n1;i++)
{
string t=s1.substr(0,i);
int x=judge(t,s1);
if(x!=-1)
{
u=t;
cnt1=x;
break;
}
}
int cnt2=judge(u,s2);
if(cnt2==-1)printf("-1\n");
else
{
int ans=cnt1/__gcd(cnt1,cnt2)*cnt2;
for(int i=1;i<=ans;i++)
printf("%s",u.c_str());
printf("\n");
}
}
return 0;
}
/*
1
ababab
abab
*/
C. No More Inversions
思路:可以先打表求一些短的序列的答案,找规律(猜结论?)。比如:
a=[1,2,3],p=[1,2,3]
a=[1,2,3,2],p=[1,3,2]
a=[1,2,3,2,1],p=[3,2,1]
a=[1,2,3,4],p=[1,2,3,4]
a=[1,2,3,4,3],p=[1,2,4,3]
a=[1,2,3,4,3,2],p=[1,4,3,2]
a=[1,2,3,4,3,2,1],p=[4,3,2,1]
这个规律你应该能看出来了吧。
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int T,n,k,a[N];
string s1,s2;
int main()
{
ios::sync_with_stdio(false);
cin>>T;
vector<int>ans;
while(T--)
{
cin>>n>>k;
int d=n-k;
if(d==0)
{
for(int i=1;i<=k;i++)
i==k?printf("%d\n",i):printf("%d ",i);
}
else
{
int t=k-(d+1);
ans.clear();
for(int i=1;i<=t;i++)
ans.push_back(i);
int p=k;
for(int i=t+1;i<=k;i++)
ans.push_back(p--);
int sz=ans.size();
for(int i=0;i<sz;i++)
i==sz-1?printf("%d\n",ans[i]):printf("%d ",ans[i]);
}
}
return 0;
}
/*
5
5 5
6 5
7 5
8 5
9 5
ans:
1 2 3 4 5
1 2 3 5 4
1 2 5 4 3
1 5 4 3 2
5 4 3 2 1
*/
D. Program
思路:不需要线段树,只要用数组维护一下每个位置前缀、后缀的最小、最大值即可。
假设x的所有变动值构成的序列为a,中间切一个区间[l,r],剩下左右两个区间,左区间用前缀来O(1)得到最值,右区间用后缀来O(1)得到最值。切掉[l,r]后会对右区间的最小、最大值造成影响,变动值为a[r]-a[l-1],只要减去这个变动值即可得到右区间新的最小~最大值。假设左区间最小最大分别为[low1,high1],右区间最小最大分别为[low2,high2],合并这两个区间最值即可求得答案。
另外,注意特判切掉的区间从1开始或从n结束的情况,这样就可能只剩左区间或者只剩右区间,这种情况下注意要和[0,0]进行合并(因为x初始为0)。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10,inf=0x7f7f7f7f;
char s[N];
int T,n,m,a[N],mi1[N],mx1[N],mi2[N],mx2[N];
int join(int l1,int r1,int l2,int r2) // 区间合并 [l1,r1]与[l2,r2]
{
if(l1>l2)swap(l1,l2),swap(r1,r2);
if(l2<=r1)return max(r1,r2)-l1+1;
else return r1-l1+1+r2-l2+1;
}
int main()
{
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>n>>m>>s+1;
int x=0;
mi1[0]=0,mx1[0]=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='+')x++;
else x--;
a[i]=x;
mi1[i]=min(mi1[i-1],a[i]);
mx1[i]=max(mx1[i-1],a[i]);
}
mi2[n+1]=a[n],mx2[n+1]=a[n];
for(int i=n;i>=1;i--)
{
mi2[i]=min(mi2[i+1],a[i]);
mx2[i]=max(mx2[i+1],a[i]);
}
int l,r,ans;
while(m--)
{
cin>>l>>r;
int low1=mi1[l-1];
int high1=mx1[l-1];
int low2=mi2[r+1];
int high2=mx2[r+1];
int d=a[r]-a[l-1]; // 切去[l,r]区间后造成的变动值
if(l==1&&r==n)ans=1; // 全切
else if(l==1&&r!=n)ans=join(0,0,low2-d,high2-d); // 只剩右边区间[r+1,n]
else if(l!=1&&r==n)ans=join(0,0,low1,high1); // 只剩左边区间[1,l-1]
else ans=join(low1,high1,low2-d,high2-d); // 切中间,剩左右两个区间
printf("%d\n",ans);
}
}
return 0;
}