xtq的神笔-赛后题解
20pts:
枚举从哪里开始以及每一笔的结束位置,统计答案。
40pts:
约定:
设
其中
70pts:
观察到一个重要性质:
若区间
如果我们固定一个右端点
这是不难证明的:
对于
对于
这也就意味着,对于每一个状态
那么对于每一个
复杂度分析:
一共枚举
事实上,由于这个上界十分宽松,这样的算法应该可以获得接近满分或者满分。
100pts:
我们每次都要重新二分找出段落,这是十分浪费时间的。
事实上,每次计算出一个
所以,我们从始至终只需要维护当前决策点的分段即可。每次计算出
每一段除了维护
此外,std中使用了st表来维护区间的
复杂度分析:
预处理st表:
维护决策点段落:由于每一时刻至多有
转移:总的转移复杂度显然是
总复杂度的上界为
此题完结。
代码:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <ctime>
using namespace std;
const int MAXN = 300005;
typedef long long ll;
int n,k,a[MAXN],bin[MAXN<<1];
inline int gcd(int a, int b)
{
int c;
while(b)
{
c = a;
a = b;
b = c%b;
}
return a;
}
int sto[MAXN][19],sta[MAXN][19],stg[MAXN][19];
inline int queyro(int l, int r)
{
int kk = bin[r-l+1];
return sto[l][kk]|sto[r-(1<<kk)+1][kk];
}
inline int querya(int l, int r)
{
int kk = bin[r-l+1];
return sta[l][kk]&sta[r-(1<<kk)+1][kk];
}
inline int queryg(int l, int r)
{
int kk = bin[r-l+1];
return gcd(stg[l][kk],stg[r-(1<<kk)+1][kk]);
}
ll f[MAXN];
struct Node
{
ll val1,val2,val3,maxf;
}pool[MAXN];
int tot,front = 0, rear = 0;
int nxt[MAXN],lst[MAXN];
inline void insert(Node t)
{
pool[++tot] = t;
nxt[rear] = tot, lst[tot] = rear;
rear = tot;
nxt[rear] = -1;
}
inline void remove(int pos)
{
nxt[lst[pos]] = nxt[pos];
if(pos!=rear) lst[nxt[pos]] = lst[pos];
if(pos==rear) rear = lst[pos];
}
inline int read()
{
char c = 0;
int res = 0, flag = 1;
while(c<'0'||c>'9')
{
c = getchar();
if(c=='-') flag = -1;
}
while(c>='0'&&c<='9')
{
res = res*10+c-'0';
c = getchar();
}
return res*flag;
}
inline void init()
{
tot = 0;
front = rear = 0;
memset(nxt,0,sizeof(nxt));
memset(lst,0,sizeof(lst));
for(int i = 0; i<MAXN; i++)
f[i] = -(1ll<<62);
nxt[0] = -1;
}
int main()
{
int T;
cin >> T;
while(T--)
{
init();
cin >> n >> k;
for(int i = 1; i<=n; i++)
a[i] = read();
for(int i = 1; i<=k; i++)
f[i-1] = read();
for(int cnt = 0, i = 1; i<=n; i <<= 1, cnt++)
for(int j = i; j<(i<<1); j++)
bin[j] = cnt;
for(int i = 1; i<=n; i++)
sto[i][0] = sta[i][0] = stg[i][0] = a[i];
for(int j = 1; (1<<j)<=n; j++)
for(int i = 1; i+(1<<j)-1<=n; i++)
{
sto[i][j] = sto[i][j-1]|sto[i+(1<<(j-1))][j-1];
sta[i][j] = sta[i][j-1]&sta[i+(1<<(j-1))][j-1];
stg[i][j] = gcd(stg[i][j-1],stg[i+(1<<(j-1))][j-1]);
}
Node tmpnode;
for(int i = k; i<=n; i++)
{
int tmppos = nxt[front];
while(tmppos>=0)
{
pool[tmppos].val1 |= a[i];
pool[tmppos].val2 &= a[i];
pool[tmppos].val3 = gcd(pool[tmppos].val3,a[i]);
tmppos = nxt[tmppos];
}
tmpnode.val1 = queyro(i-k+1,i);
tmpnode.val2 = querya(i-k+1,i);
tmpnode.val3 = queryg(i-k+1,i);
tmpnode.maxf = f[i-k];
insert(tmpnode);
tmppos = nxt[front];
while(nxt[tmppos]>=0&&tmppos>=0)
{
if(pool[nxt[tmppos]].val1==pool[tmppos].val1&&
pool[nxt[tmppos]].val2==pool[tmppos].val2&&
pool[nxt[tmppos]].val3==pool[tmppos].val3)
{
pool[tmppos].maxf = max(pool[tmppos].maxf,pool[nxt[tmppos]].maxf);
remove(nxt[tmppos]);
}
tmppos = nxt[tmppos];
}
tmppos = nxt[front];
while(tmppos>=0)
{
f[i] = max(f[i],pool[tmppos].maxf+pool[tmppos].val1+
pool[tmppos].val2+pool[tmppos].val3);
tmppos = nxt[tmppos];
}
}
cout << f[n] << endl;
}
return 0;
}