牛客OI月赛12-提高组题解
当天晚上被\(loli\)要求去打了某高端oj部分原创的模拟赛,第二天看了牛客的题觉得非常清真,于是就去写了
不难发现现场写出\(260\text{pts}\)并不需要动脑子,而且\(260\text{pts}\)甚至还有\(rk2\),感觉没打非常吃亏
A.小w的进制转换
大概理解一下就是询问\(1\)到\(n\)里有多少个数的二进制表示是反回文形式的,即对称位置是相反的,比如\(1001,101010\)
之后根据题意,这个反回文的二进制串长度必须是偶数(因为长度为奇数的串对称中心和对称中心不相反)
也不难发现这个反回文串确定了前一半后面就确定了
考虑枚举这个反回文的串的长度,显然长度小于\(n\)的长度的串,我们只需要让最高填\(1\),最低位填\(0\),之后除去最高位的前\(\frac{len-2}{2}\)随便填即可
对于长度等于\(n\)的串,还是最高填\(1\),最低位填\(0\),除去最高位的前\(\frac{len-2}{2}\)位填一个严格小于\(n\)的前\(\frac{len-2}{2}\)位的数,这样就能保证严格小于\(n\)
之后再特判一下前\(\frac{len-2}{2}\)位和\(n\)的前\(\frac{len-2}{2}\)位相等时的时候,反对称过去的数是否超过\(n\)
代码好像写得有点丑了
#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
LL b[65];
LL n,ans;
inline void calc(int bit) {
if(bit&1) return;
ans+=(1ll<<((bit-2)/2));
}
int a[64];int tot;
inline int solve() {
b[0]=1;int W=2*tot+1;
for(re int i=0;i<=tot;++i) b[W-i]=b[i]^1;
LL m=0;
for(re int i=0;i<=W;++i)
m<<=1ll,m|=b[i];
return m<=n;
}
inline void Calc() {
scanf("%lld",&n);;
int now=0;tot=0;ans=0;
for(re LL i=63;i>=0;--i) {
if(!now) {
if(n>>i&1) {
now=1;
if((i-1)&1) continue;
for(re LL j=i-1;j>=0;--j) {
if(tot*2>=i-1) continue;
b[++tot]=n>>j&1;
}
LL k=0;
for(re LL j=1;j<=tot;++j)
k|=b[j],k<<=1ll;
k>>=1ll;ans=k;
if(!tot) ++ans;else ans+=solve();
}
continue;
}
if(i>0) calc(i+1);
}
std::cout<<ans;puts("");
}
int main() {
int T;scanf("%d",&T);
while(T--) Calc();
return 0;
}
B.小doge的快乐阳光跑
题意:给一张图,求一个权值和路径最小的移动序列,使得移动序列包含两个给定的子序列。
发现点数只有\(10^3\),边数也只有\(10^4\),图相当稀疏,所以完全可以跑\(n\)遍单源最短路,求出所有点对的之间的最短路
之后搞一个\(dp\)就完事了,设\(dp_{0/1,i,j}\)表示当前在第\(1/2\)个子序列的第\(i\)个位置,另一个子序列已经经过了前\(j\)个位置
转移显然,就是枚举一下下一个点去哪里
#include<bits/stdc++.h>
#define re register
#define LL long long
#define mp std::make_pair
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
typedef std::pair<int,int> pii;
const int maxn=1e3+5;
struct E{int v,nxt,w;}e[maxn*20];
int d[maxn][maxn];
int n,m,num,head[maxn],vis[maxn],a[maxn],b[maxn],A,B;
inline void add(int x,int y,int w) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=w;
}
LL dp[2][105][105];
std::priority_queue<pii,std::vector<pii>,std::greater<pii> > q;
inline void Dij(int s) {
for(re int i=1;i<=n;++i) d[s][i]=1e9,vis[i]=0;
d[s][s]=0,q.push(mp(d[s][s],s));
while(!q.empty()) {
int k=q.top().second;q.pop();
if(vis[k]) continue;vis[k]=1;
for(re int i=head[k];i;i=e[i].nxt)
if(d[s][e[i].v]>d[s][k]+e[i].w) {
d[s][e[i].v]=d[s][k]+e[i].w;
q.push(mp(d[s][e[i].v],e[i].v));
}
}
}
int main() {
n=read(),m=read();
for(re int x,y,z,i=1;i<=m;i++)
x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
for(re int i=1;i<=n;i++) Dij(i);
A=read();for(re int i=1;i<=A;i++) a[i]=read();
B=read();for(re int i=1;i<=B;++i) b[i]=read();
memset(dp,20,sizeof(dp));
dp[0][1][0]=0,dp[1][0][1]=0;
for(re int t=1;t<A+B;++t)
for(re int j=0;j<=t&&j<=A;++j) {
re int k=t-j;
if(k>B) continue;
if(j<A) dp[0][j+1][k]=min(dp[0][j+1][k],dp[0][j][k]+d[a[j]][a[j+1]]),
dp[0][j+1][k]=min(dp[0][j+1][k],dp[1][j][k]+d[b[k]][a[j+1]]);
if(k<B) dp[1][j][k+1]=min(dp[1][j][k+1],dp[0][j][k]+d[a[j]][b[k+1]]),
dp[1][j][k+1]=min(dp[1][j][k+1],dp[1][j][k]+d[b[k]][b[k+1]]);
}
printf("%lld\n",min(dp[1][A][B],dp[0][A][B]));
return 0;
}
C.区间异或和异或区间最大值异或区间最小值
题意就是在给定的一个序列里找到一个区间,让上面那个东西最大
有\(60\rm pts\)是送的,写几个\(\rm subtask\)拼一拼就有了
考虑正解,考虑率大力分治一波,对于区间\([l,r]\)我们考虑一下如何合并\([l,mid]\)和\([mid+1,r]\)
我们把左区间所有后缀以及右区间所有前缀的异或和、最大值、最小值都扫出来,我们提前把最大值和最小值都给异或进去
考虑合并掉两个区间之后,两个最大值中较小的那一个就不是最大值了,我们需要把它异或回来;两个最小值中较大的也不是最小值了,也需要把它异或回来
于是我们考虑从右区间里拿出一个数来,作为最大值较小的去和左区间匹配
显然可以排序之后利用单调性开一个指针,把左区间比这个最大值大的都扫进来,放到一个数据结构里,显然这种数据结构是\(trie\)
之后按照最小值讨论一波
-
左区间的最小值作为最小值,那么我们需要把右区间的最小值给异或回来,去查最小值小于右区间最小值中,异或上这个数最大的
-
右区间的最小值作为最小值,那么需要把左区间的最小值给异或回来,我们可以对左区间维护一个把最小值异或回来的\(trie\),在这个\(trie\)里查最小值大于右区间最小值中,异或上这个数最大的
我们发现按照最大值把这些数加入\(trie\)的过程中最小值也是单调的,于是把\(trie\)可持久化一下就可以查区间异或最大值了
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
#pragma GCC optimize(3)
#pragma GCC optimize("-fcse-skip-blocks")
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=1e5+5;
const int Bit=30;
struct Seg {int mn,mx,x;}b[2][maxn];
int n,a[maxn],c[maxn],cnt,tot,ans;
int rt[2][maxn],son[maxn*32][2],d[maxn*32];
inline int max(int a,int b) {return a>b?a:b;}
inline int min(int a,int b) {return a<b?a:b;}
int ins(int pre,int x,int w) {
int now=++cnt;
d[now]=d[pre]+1;
if(w==-1) return now;
son[now][0]=son[pre][0],son[now][1]=son[pre][1];
son[now][x>>w&1]=ins(son[pre][x>>w&1],x,w-1);
return now;
}
int query(int p1,int p2,int x,int w) {
if(w==-1) return 0;
int t=x>>w&1;
int now=d[son[p2][t^1]]-d[son[p1][t^1]];
if(now) return (1<<w)+query(son[p1][t^1],son[p2][t^1],x,w-1);
return query(son[p1][t],son[p2][t],x,w-1);
}
inline int find(int x) {
int l=1,r=tot,now=0;
while(l<=r) {
int mid=l+r>>1;
if(c[mid]<=x) l=mid+1,now=mid;
else r=mid-1;
}
return now;
}
void solve(int l,int r) {
if(l==r) return;
int mid=l+r>>1;
solve(l,mid),solve(mid+1,r);
int lenl=mid-l+1,lenr=r-mid;
for(re int i=mid+1;i<=r;++i) {
b[0][i-mid].mn=min(a[i],b[0][i-1-mid].mn);
b[0][i-mid].mx=max(a[i],b[0][i-1-mid].mx);
b[0][i-mid].x=a[i]^b[0][i-mid-1].x;
}
for(re int i=mid;i>=l;--i) {
b[1][mid+1-i].mn=min(a[i],b[1][mid-i].mn);
b[1][mid+1-i].mx=max(a[i],b[1][mid-i].mx);
b[1][mid+1-i].x=a[i]^b[1][mid-i].x;
}
for(re int i=1;i<=lenl;++i) b[1][i].x^=(b[1][i].mn^b[1][i].mx);
for(re int i=1;i<=lenr;++i) b[0][i].x^=(b[0][i].mn^b[0][i].mx);
cnt=0,tot=0;int now=lenl;
for(re int i=lenr;i;--i) {
while(now&&b[1][now].mx>=b[0][i].mx) {
++tot;
rt[0][tot]=ins(rt[0][tot-1],b[1][now].x,Bit);
rt[1][tot]=ins(rt[1][tot-1],b[1][now].x^b[1][now].mn,Bit);
c[tot]=b[1][now--].mn;
}
if(tot) {
int x=find(b[0][i].mn);
ans=max(ans,query(rt[0][0],rt[0][x],b[0][i].x^b[0][i].mx^b[0][i].mn,Bit));
ans=max(ans,query(rt[1][x],rt[1][tot],b[0][i].x^b[0][i].mx,Bit));
}
}
for(re int i=1;i<=tot;i++) rt[0][i]=rt[1][i]=0;
cnt=0;tot=0;now=lenr;
for(re int i=lenl;i;--i) {
while(now&&b[0][now].mx>=b[1][i].mx) {
++tot;
rt[0][tot]=ins(rt[0][tot-1],b[0][now].x,Bit);
rt[1][tot]=ins(rt[1][tot-1],b[0][now].x^b[0][now].mn,Bit);
c[tot]=b[0][now--].mn;
}
if(tot) {
int x=find(b[1][i].mn);
ans=max(ans,query(rt[0][0],rt[0][x],b[1][i].x^b[1][i].mx^b[1][i].mn,Bit));
ans=max(ans,query(rt[1][x],rt[1][tot],b[1][i].x^b[1][i].mx,Bit));
}
}
for(re int i=1;i<=tot;i++) rt[0][i]=rt[1][i]=0;
}
int main() {
n=read();
for(re int i=1;i<=n;i++) a[i]=read(),ans=max(ans,a[i]);
b[0][0].mn=b[1][0].mn=1e9;
solve(1,n);
printf("%d\n",ans);
return 0;
}