牛客练习赛53 部分题解
T1:动态规划,f[i][0/1]表示前i位,最后一位是否是c的方案数。
曾经妄想用组合数学去算,由于无法解决重复计算问题最终放弃转而思考动态规划,成功AC
代码略
T2:数学+卡时限
简单的“变化求和顺序” ,经典变形: \(\sum_{i=1}^N \sum_{j=i}^N\) = \(\sum_{j=1}^N \sum_{i=1}^j\) 即i<=j
然后就可以分块暴力计算了,复杂度是调和级数的级别,注意此题不能用快速幂,\(log^2\)会超时
T3:bitset(位运算)优化集合的处理
我的做法不同于正解
考虑把每位拆开来看,0或1都会筛去一些不合法的数,'_'则不会筛去任何数
考虑筛去数的过程,其实就是对m个集合取并集,可以用位运算&优化
考试时想到了这种做法,但没有想到用bitset优化,而且我当时以为bitset会TLE
复杂度 \(O(n*m*q/32)\) 300010001000/32刚好能过
代码:
#include<bits/stdc++.h>
using namespace std;
#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
const int inf=0x3f3f3f3f,N=1000+10;
int n,m;
bitset<N>t[N][2];
char s[N];
void read(int &x){
x=0;char c=getchar(),f=1;
while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
x*=f;
}
int main(){
//freopen("input.txt","r",stdin);
read(n),read(m);
go(i,1,n){
scanf("%s",s+1);
go(j,1,m){
if(s[j]=='0') t[j][0][i]=1;
else t[j][1][i]=1;
}
}
int Q;read(Q);
bitset<N>ans;
while(Q--){
scanf("%s",s+1);
ans.set();
go(i,1,m){
if(s[i]=='_') continue;
if(s[i]=='0') ans&=t[i][0];
else ans&=t[i][1];
}
printf("%d\n",ans.count());
}
return 0;
}
T5:线段树+离线询问
异或的性质:异或两次相当于没有异或
推出:sum[r]^sum[l-1]=l到r的异或和
因此我们可以求出对于每个右端点,离他最近且异或和为0的左端点
然后线段树维护最小值,详见代码
#include<bits/stdc++.h>
using namespace std;
#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
const int inf=0x3f3f3f3f,N=500000+10;
int n,m,d[N],p[N*2],ans[N];
bool vis[N*2];
struct tree{
int l,r,mn;
#define l(i) t[i].l
#define r(i) t[i].r
#define mn(i) t[i].mn
#define ls rt<<1
#define rs rt<<1|1
}t[N<<2];
struct Question{
int l,r,id;
friend bool operator <(const Question &a,const Question &b){
return a.r<b.r;
}
}q[N];
void build(int rt,int l,int r){
l(rt)=l,r(rt)=r,mn(rt)=inf;
if(l==r) return;
int mid=l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
}
inline void push_up(int rt){ mn(rt)=min(mn(ls),mn(rs)); }
void add(int rt,int pos,int k){
if(l(rt)==r(rt)){
mn(rt)=min(mn(rt),k);
return;
}
int mid=l(rt)+r(rt)>>1;
if(pos<=mid) add(ls,pos,k);
else add(rs,pos,k);
push_up(rt);
}
int query(int rt,int x,int y){
if(l(rt)>=x&&r(rt)<=y) return mn(rt);
int mid=l(rt)+r(rt)>>1;
int ans=inf;
if(x<=mid) ans=min(ans,query(ls,x,y));
if(y>mid) ans=min(ans,query(rs,x,y));
return ans;
}
void read(int &x){
x=0;char c=getchar(),f=1;
while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
x*=f;
}
int main(){
//freopen("input.txt","r",stdin);
read(n),read(m);
vis[0]=1;
int sum=0,x;
go(i,1,n){
read(x);
sum^=x;
if(vis[sum]) d[i]=p[sum]+1;
p[sum]=i;
vis[sum]=1;
}
go(i,1,m) read(q[i].l),read(q[i].r),q[i].id=i;
sort(q+1,q+m+1);
int pos=1;build(1,1,n);
go(i,1,n){
if(d[i]) add(1,d[i],i-d[i]+1);
while(q[pos].r==i){//注意这里是while,因为可能有右端点重合的情况
ans[q[pos].id]=query(1,q[pos].l,q[pos].r);
if(++pos==m+1) break;
}
}
go(i,1,m) printf("%d\n",ans[i]==inf?-1:ans[i]);
return 0;
}