[CodeTON Round 2][Codeforces 1704G. Mio and Lucky Array]
题目链接:1704G - Mio and Lucky Array
题目大意:给定数组
为了方便起见,我们默认下标从
首先要注意到,在一次操作后,
- 看到变化量是
1,-2,3,-4,\cdots 有点烦,因为有一个(-1)^k 的影响因素在,所以在想如果把偶数位取反就可以把变化量变成1,2,3,\cdots 了。 - 如果变化量是
1,2,3,\cdots 的话,那么取一个二阶差分就可以找到一个不变量。二阶差分的式子为d_i=(a_{i}-a_{i-1})-(a_{i-1}-a_{i-2})=a_i-2a_{i-1}+a_{i-2} 。 - 发现与其取反后二阶差分,不如直接在式子上进行更改,把对应系数取反。于是就找到了原操作的不变量
a_i+2a_{i-1}+a_{i-2} 。
那么令
在这里就需要思考一下在操作过后如何判断
由于 bitset 判断所有
为了判断一个起点是否合法,暴力的话可以有如下代码:
for(int st=0;st<=n-m;st++)
for(int i=2;i<m;i++)
ok[st]&=(f[st+i]==g[i])|(f[st+i]==g[i]-1);
既然要利用 bitset,就需要改变枚举的先后顺序,以便一次性对所有的
for(int i=2;i<m;i++)
for(int st=0;st<=n-m;st++)
ok[st]&=(f[st+i]==g[i])|(f[st+i]==g[i]-1);
于是对数组 vector,对每个数值记录数组内有哪些位置等于指定值,就可以快速进行处理:
for(int i=2;i<m;i++){
bitset<N>t;
for(auto j:d[g[i]])t[j]=1;
for(auto j:d[g[i]-1])t[j]=1;
ok&=t>>i;
}
当然为了避免超时,还需要对每个相同的
这样我们就能在
对每个符合条件的
这样一来,我们就需要判断在加法操作与减法操作的差为
注意到,由于所有的减法操作减去的都是偶数,所以
设最大的加法操作为
同样设绝对值最大的减法操作为
可以发现在上述式子中,
关于构造的部分,直接暴力找到一个满足条件的
#include<bits/stdc++.h>
using namespace std;
#define N 200010
#define LL long long
int T,n,m,a[N];
LL b[N],f[N];
pair<LL,int>g[N];
bitset<N>ok,t;
map<LL,vector<int>>mp;
LL F(LL A,LL B,LL C,LL x){return A*x*x+B*x+C;}
bool check(int st)
{
LL d0=b[0]-a[st],d1=b[1]-a[st+1];
LL k=d0+d1,M=st&1?st:(st+1),M_=st&1?(st+1):st;
LL nx=st/2+1,ny=(st+3)/2;
LL l=max(0ll,-k),r=min(nx,ny-k);
LL o=d0&1;
if(l%2!=o)l++;
if(r%2!=o)r--;
if(l>r)return false;
LL A=-2,B=M-2ll*k+2,C=k-k*k;
LL x0=-B/(2ll*A);
LL mn=1e18,mx=-mn;
for(LL i=x0-5;i<=x0+5;i++)if(i%2==o && l<=i && i<=r)mx=max(mx,F(A,B,C,i));
mx=max(mx,F(A,B,C,l));
mx=max(mx,F(A,B,C,r));
A=2,B=2ll*k-M_-1,C=k*k-(M_+1)*k;
x0=-B/(2ll*A);
for(LL i=x0-5;i<=x0+5;i++)if(i%2==o && l<=i && i<=r)mn=min(mn,F(A,B,C,i));
mn=min(mn,F(A,B,C,l));
mn=min(mn,F(A,B,C,r));
if(d0>mx || d0<mn)return false;
LL x=-1;
for(LL i=l;i<=r;i++)if(i%2==o){
mn=F(A,B,C,i);
mx=F(-2,M-2ll*k+2,k-k*k,i);
if(mn<=d0 && d0<=mx){x=i;break;}
}
set<int>ans;
LL s=0,y=x+k;
for(LL i=0;i<x;i++)s+=2ll*i+1,ans.insert(st-2ll*i);
for(LL i=0;i<y;i++)s-=2ll*i,ans.insert(st-2ll*i+1);
for(LL i=x-1;i>=0 && s<d0;i--){
s-=2ll*i+1;
ans.erase(st-2ll*i);
while(s+M>d0)M-=2;
s+=M;
ans.insert(st-M+1);
M-=2;
}
for(LL i=y-1;i>=0 && s>d0;i--){
s+=2ll*i;
ans.erase(st-2ll*i+1);
while(s-M_<d0)M_-=2;
s-=M_;
ans.insert(st-M_+1);
M_-=2;
}
for(LL i=2;i<m;i++)if(f[st+i]==g[i].first-1)ans.insert(st+i);
printf("%d\n",(int)ans.size());
for(auto i:ans)printf("%d ",i+1);
printf("\n");
return true;
}
void init()
{
mp.clear();
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]),ok[i]=1;
scanf("%d",&m);
for(int i=0;i<m;i++)scanf("%lld",&b[i]);
for(int i=2;i<n;i++)mp[f[i]=a[i]+2*a[i-1]+a[i-2]].push_back(i);
for(int i=2;i<m;i++)g[i]=make_pair(b[i]+2ll*b[i-1]+b[i-2],i);
sort(g+2,g+m);
for(int i=2;i<m;){
t.reset();
for(auto j:mp[g[i].first])t[j]=1;
for(auto j:mp[g[i].first-1])t[j]=1;
int j=i;
while(j<m && g[j].first==g[i].first)ok&=t>>g[j].second,j++;
i=j;
}
for(int i=2;i<m;i++)g[i]=make_pair(b[i]+2ll*b[i-1]+b[i-2],i);
for(int i=0;i<=n-m;i++)if(ok[i])
if(check(i))return;
printf("-1\n");
}
int main()
{
scanf("%d",&T);
while(T--)init();
}
求二次函数极值的部分为了省脑子直接在实际极值点的左右选
另外,初步确定满足第一个条件的 bitset。