2025牛客多校第六场(持续更新)
L
题意:构造一个长度为2n的括号序列,满足字典序最小且在给出的q个区间内都至少有一个左括号
思路:
为了满足字典序尽量小,一定想把左括号放在靠左的位置
为了满足区间至少有一个左括号,需要贪心地按左端点从大到小排序,将左括号依次放在区间的左端点。如果区间已经有左括号,直接跳过
-1的情况是左括号数量>n或不能构成合法括号序列
void solve(){
int n,q;cin>>n>>q;
vector<int>a(2*n+2,-1);
vector<pii>line;
while(q--){
int l,r;cin>>l>>r;
line.pb({l,r});
}
sort(line.begin(),line.end());
int now=2*n+1;
int cnt=0;
for(int i=line.size()-1;i>=0;i--){
int l=line[i].fi,r=line[i].se;
if(r<now){
a[l]=1;
cnt++;
now=l;
}
}
if(cnt>n){
cout<<-1<<endl;
return;
}
for(int i=1;i<=2*n,cnt!=n;i++){
if(a[i]!=1){
a[i]=1;cnt++;
}
}
int pre=0;
rep(i,1,2*n){
pre+=a[i];
if(pre<0){
cout<<-1<<endl;return;
}
}
rep(i,1,2*n){
if(a[i]==1){
cout<<'(';
}else cout<<')';
}
cout<<endl;
}
K
题意:给定一个长度为n的数组,可以选取一个任意的区间使其所有数加上一个非负整数,求操作后数组的最大gcd
思路:
有结论
gcd(a,b)=gcd(a,a-b)

先考虑给全部数都+k
那么显然=>gcd(a[1]+k,....a[n]+k)=gcd(a[1]+k,a[2]-a[1],...)
只需要求数组a差分数组的gcd即可
再考虑某个区间+k
显然a[1]或a[n]是不在要操作的区间里的
因此数组的gcd一定是这两个数的约数
不妨枚举这两个数产生的约数p
然后确定要+k的区间的左端点和右端点:从左至右第一个模p不为0和从右至左第一个模p不为0
如果确定的该区间的数模p有不同的模数,即不可能通过加上相同的k使gcd=p
确定gcd最大值即可
(PS:非常奇怪的一个点是如果下面这个代码用__gcd()会WA%75 ,而用gcd()就能AC,谁能告诉我为什么,我是不是被评测机做局了?)
void work(vector<int>&v,int x){
for(int i=1;i*i<=x;i++){
if(x%i==0){
v.pb(i);
if(i*i!=x)v.pb(x/i);
}
}
}
int n;
bool check(int fact,vector<int>&a){
int l=1,r=n;
for(int i=1;i<=n;i++){
if(a[i]%fact){
l=i;break;
}
}
for(int i=n;i>=1;i--){
if(a[i]%fact){
r=i;break;
}
}
if(l==-1)return true;
for(int i=l;i<=r-1;i++){
if((a[i]%fact)!=(a[i+1]%fact))return false;
}
return true;
}
void solve(){
cin>>n;
set<int>st;
vector<int>a(n+1);
rep(i,1,n)cin>>a[i],st.insert(a[i]);
if(st.size()==1){
cout<<0<<endl;return;
}
int G=a[2]-a[1];
for(int i=3;i<=n;i++){
G=__gcd(G,abs(a[i]-a[i-1]));
}
vector<int>v;
work(v,a[1]);
work(v,a[n]);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int ans=1;
for(auto fact:v){
if(check(fact,a)) ans=max(ans,fact);
}
cout<<max(ans,G)<<endl;
}
M
这题思路不难,但是代码太难调了
题意:给定大小为n的数字集合,和进制m。将集合恰好分为两个集合,分别用集合的数组成一个m进制的数。
要求求得这两个m进制数的最小差值(答案输出十进制下)
思路:
先将数组排序
显然比较两个数的大小,只需贪心地从高位到低位比较相同位大小即可
这题不能把A,B两个十进制数先算出来然后在相减。而且还要开高精度
因此设置一个an数组表示最优的差值数组
- n为偶数时,我们两个数的首位想取差值最小的,然后首位较小的从右往左取数,首位较大的从左往右取数
而对于例子:3 4 5 6 , 由于数组差值相同 , 我们尽量取靠近中间的两个相邻数对
显然46,53 比36 ,45 的差值要更小
在差值相同的情况下,从中间开始,取最近的左边/右边的最小差值对,当做首位,比较两个结果哪个更优
- n为奇数时,可能会有前导0的影响
如果没有0,显然一个数长度为 n/2 ,一个数长度为 n/2 +1 ,显然贪心地,大的数从左往右取,小的数从右往左取
那么,如果有0 , 可以 把0去掉 ,然后n变为偶数 ,去计算
也可以把次低位看作首位,然后按没有0的情况计算
最后计算答案
代码细节真的很多
#define int __int128
#define gc getchar()
#define cin(a) a=read()
#define M 10000011
int read(){
int x=0;bool fl=0;char s=gc;
while(!isdigit(s)){if(s=='-')fl=1;s=gc;} // 处理符号位
while(isdigit(s))x=(x<<1)+(x<<3)+s-'0',s=gc; // 计算数值,相当于x = x*10 + (s-'0')
return fl?-x:x;
}
void Print(int x){
if(x/10) Print(x/10); // 递归输出高位
putchar(x%10+'0'); // 输出当前位
}
int n,m;
int an[M];
int a[M];
int t[M];
void change(int x){
//最小差值对:{x,x+1}
vector<int>b;
int cnt=0;
rep(i,1,n){
if(i==x||i==x+1)continue;
b.pb(a[i]);
cnt++;
}
for(int i=0;i<cnt/2;i++){
t[i] = b[cnt/2-i-1] - b[cnt/2+i];
}
t[cnt/2] = a[x+1] - a[x];
for(int i=cnt/2;i>=0;i--){
//从高位到低位枚举
if(t[i]==an[i])continue;
if(t[i]<an[i]){
for(int j=0;j<=cnt/2;j++) an[j] = t[j];
break;
}
if(t[i]>an[i])break;
}
}
void work(){
//偶数情况 1 2 3 4 5 6
int mi =M,p1=0,p2=0;
rep(i,1,n-1){
mi =min(mi,a[i+1]-a[i]);
}
for(int i=1;i<=n/2;i++){
if(a[i+1]-a[i]==mi)p1=i;
}
for(int i=n-1;i>n/2;i--){
if(a[i+1]-a[i]==mi)p2=i;
}
if(p1)change(p1);
if(p2)change(p2);
}
void solve(){
cin(n);cin(m);
rep(i,1,n)cin(a[i]);
a[n+1] = 0;//奇数情况有用
sort(a+1,a+1+n);
if(n%2==0){
an[n/2-1]=M;
work();
}else{
int flag=0;
if(a[1]==0){
swap(a[1],a[2]);
for(int i=0;i<=n/2;i++){
an[i] = a[n/2+1-i] - a[n/2+1+1+i];
}
//两个数的位数分别为n/2+1 ,n/2
//差值数组长度为n/2+1 ->[0 ...n/2 -1,n/2]
//n/2位置上分别只有一个正数和一个0
//不妨进位
an[n/2-1] += an[n/2]*m;
for(int i=2;i<=n;i++) a[i]=a[i+1];
n--;
flag=1;
work();
}
if(!flag){
for(int i=0;i<=n/2;i++){
an[i] =a[n/2+1-i] -a[n/2+2+i];
}
// an[n/2-1] +=an[n/2]*m;
}
}
int ans=0;
int pp=1;
//计算差值数组an [0 n/2-1]
for(int i=0;i<(n+1)/2;i++){
ans =(ans%mod + an[i]*pp%mod +mod+mod)%mod;
pp=pp*m%mod;
}
Print(ans);
// putchar('\n');
cout<<endl;
for(int i=0;i<=n+1;i++){
an[i]=a[i]=t[i]=0;
}
}
(PS:从别人代码偷的简单__int128精度计算)
#define int __int128
#define gc getchar()
#define cin(a) a=read()
int read(){
int x=0;bool fl=0;char s=gc;
while(!isdigit(s)){if(s=='-')fl=1;s=gc;} // 处理符号位
while(isdigit(s))x=(x<<1)+(x<<3)+s-'0',s=gc; // 计算数值,相当于x = x*10 + (s-'0')
return fl?-x:x;
}
void Print(int x){
if(x/10) Print(x/10); // 递归输出高位
putchar(x%10+'0'); // 输出当前位
}

浙公网安备 33010602011771号