大江东去,浪淘尽,千古风流人物。故垒西边,人道是,三国周郎赤壁。乱石穿空,惊涛拍岸,卷起千堆雪。江山如画,一时多少豪杰。遥想公瑾当年,小乔初嫁了,雄姿英发。羽扇纶巾,谈笑间,樯橹灰飞烟灭。故国神游,多情应笑我,早生华发。人生如梦,一尊还酹江月。

牛客练习赛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;
}
posted @ 2019-10-12 11:49  White_star  阅读(190)  评论(0编辑  收藏  举报
}