【考试总结】2022-01-23

合成小丹

考虑计算答案所属于的最小集合,这里集合用二进制数字表示,大小关系以高位为先

从高到低确定每个数位是不是能从集合中删去且满足删去之后仍满足最小答案的母集

考虑一个数字 x 是最终答案的母集的条件为 在初始数组中有一个数字是 x 的子集或者有至少 22x+1 的子集

如果仍然不满足条件可以继续向 x 末尾添加 1 元素并将需求的数字个数翻倍

如果需求在某一时刻被满足可以直接判真因为剩下的数字可以删掉

此时复杂度为 Θ(nwlogn),按照 ii+3 的方式循环展开即可 通过本题

有方法去掉复杂度中的 logn,但是并没有掌握

Code Display
const int N=1e5+10;
ll a[N];
int n,w;
bool vis[N];
inline int calc(ll x){
	int num=0,up=n/4*4;
	if(n%4>=1){
		if(!vis[up+1]&&(a[up+1]|x)==x){
			vis[up+1]=1;
			++num;
		}
	}
	if(n%4>=2){
		if(!vis[up+2]&&(a[up+2]|x)==x){
			vis[up+2]=1;
			++num;
		}
	}
	if(n%4>=3){
		if(!vis[up+3]&&(a[up+3]|x)==x){
			vis[up+3]=1;
			++num;
		}
	}
	for(int i=0;i<up;i+=4){
		if(!vis[i+1]&&(a[i+1]|x)==x){
			vis[i+1]=1;
			++num;
		}
		if(!vis[i+2]&&(a[i+2]|x)==x){
			vis[i+2]=1;
			++num;
		}
		if(!vis[i+3]&&(a[i+3]|x)==x){
			vis[i+3]=1;
			++num;
		}
		if(!vis[i+4]&&(a[i+4]|x)==x){
			vis[i+4]=1;
			++num;
		}
	} return num;
}
inline bool check(ll x){
	int left=n,cnt=1;
	memset(vis,0,sizeof(vis));
	while(1){
		int num=calc(x); cnt-=num;
		if(cnt<=0) return 1; //delete all the other
		cnt<<=1;
		if(cnt>left) return 0;
		x=x<<1|1;	
	}
}
signed main(){
	freopen("merge.in","r",stdin); freopen("merge.out","w",stdout);
	int T=read(); while(T--){
		n=read(); w=read(); rep(i,1,n) a[i]=read<ll>();
		ll ans=1ll<<w; --ans;
		for(int i=w-1;i>=0;--i) if(check(ans^(1ll<<i))) ans^=1ll<<i;
		print(ans);
	}
	return 0;
}

路过中丹

判定区间合法的条件是存在一个奇回文串或者每个位置所处于的偶数长度回文串端点都不小于 ql

奇数长度回文串判定等价于存在两个相同字符间隔为 2,这在扫描线过程中用 BIT 可以简单维护

另一种情况用线段树维护每个字符对应的最大的偶数长度回文串左端点的值,添加一个右端点修改其作为右端点的最短偶数长度回文串中的所有字符的左端点值即可

关于不是最短的时候为什么不会产生更新的证明考虑反证,还是有一个 case 有点麻烦的,需要迭代一下

找到左端点的一个方法是 manacher,但是在 PAM 上硬跳也是可以的,逐等差数列找是不是存在偶数即可

使用一个支持区间取 max,区间查询最小值的线段树即可维护

Code Display
const int N=1e6+10;
int n,ans[N];
struct BIT{
	int c[N];
	inline void insert(int x){for(;x<=n;x+=x&(-x)) c[x]++; return ; }
	inline int query(int x){int res=0; for(;x;x-=x&(-x)) res+=c[x]; return res;}
}T;
struct Seg{
	#define ls p<<1
	#define rs p<<1|1
	int mn[N<<2],tag[N<<2];
	inline void push_down(int p){
		if(tag[p]){
			ckmax(mn[ls],mn[p]); ckmax(mn[rs],mn[p]);
			ckmax(tag[ls],tag[p]); ckmax(tag[rs],tag[p]);
			tag[p]=0;
		} return ;
	}
	inline void push_up(int p){mn[p]=min(mn[ls],mn[rs]); return ;}
	inline void upd(int p,int l,int r,int st,int ed,int v){
		if(st<=l&&r<=ed) return ckmax(mn[p],v),ckmax(tag[p],v);
		int mid=(l+r)>>1; push_down(p);
		if(st<=mid) upd(ls,l,mid,st,ed,v); if(ed>mid) upd(rs,mid+1,r,st,ed,v);
		return push_up(p);
	}
	inline int query(int p,int l,int r,int st,int ed){
		if(st<=l&&r<=ed) return mn[p]; int mid=(l+r)>>1; push_down(p);
		if(ed<=mid) return query(ls,l,mid,st,ed);
		if(st>mid) return query(rs,mid+1,r,st,ed);
		return min(query(ls,l,mid,st,ed),query(rs,mid+1,r,st,ed));
	}
}Seg;
ull hs[N],pw[N],rev_hs[N];
vector<pair<int,int> > qu[N];
char s[N];
inline ull get(int l,int r){return hs[r]-hs[l-1]*pw[r-l+1];}
inline ull rev_get(int l,int r){return rev_hs[l]-rev_hs[r+1]*pw[r-l+1];}
inline bool check(int l,int r){
	int mid=(l+r)>>1;
	if((r-l)&1) return get(l,mid)==rev_get(mid+1,r);
	else return get(l,mid-1)==rev_get(mid+1,r);
}
signed main(){ 
	freopen("pass.in","r",stdin); freopen("pass.out","w",stdout);
	n=read(); scanf("%s",s+1); pw[0]=1;
	rep(i,1,n) hs[i]=hs[i-1]*13331+s[i],pw[i]=pw[i-1]*13331;
	int Q=read();
	Down(i,n,1) rev_hs[i]=rev_hs[i+1]*13331+s[i];
	for(int i=1;i<=Q;++i){
		int ql=read(),qr=read();
		qu[qr].push_back(make_pair(ql,i));
	}
	for(int i=1;i<=n;++i){
		if(i>2&&s[i]==s[i-2]) T.insert(i-2);
		for(int j=i-1;j>=max(1,i-20000);j-=2) if(check(j,i)){Seg.upd(1,1,n,j,i,j); break;}
		for(auto t:qu[i]){
			if(t.fir==i-1) ans[t.sec]=s[i]==s[i-1];
			else{
				ans[t.sec]=T.query(i-1)-T.query(t.fir-1);
				if(!ans[t.sec]){
					if(Seg.query(1,1,n,t.fir,i)>=t.fir) ans[t.sec]=1;
				}
			}
		}
	}
	for(int i=1;i<=Q;++i) putchar(ans[i]?'1':'0');
	return 0;
}

膜拜大丹

如果走的环长大于 2 必然在路径中有一对 Ai,Bj 是互相可达的,所以简化有向路径为两元环

n1 让每个 Ai 配对的点数量最多,使用 std::set 维护所有能选的 Bj,每次挑出来最大的 Bj 减小

标算的做法是将带边数限制最大匹配问题先转化成最大独立集,在 (i,ai) 放一个 ci 权值的点,在 (bj,j) 放一个权值为 dj 的点

找到一个从 (1,1)(n,m) 的“方格路径”(区别于走平行于坐标轴的直线的路径),上面的 A 类点和下方的 B 类点权值之和就是独立集大小

有朴素 dp 和各种优化,关键一步是选定红点之后路径一定是贴下界走

Code Display
const int N=5e5+10;
int a[N],b[N],c[N],d[N],n,m,ans;
vector<int> pos[N];
set<int> st;
signed main(){
	freopen("worship.in","r",stdin); freopen("worship.out","w",stdout);
	n=read(); m=read(); 
	rep(i,1,n) a[i]=read(); rep(i,1,m) b[i]=read(),pos[b[i]].push_back(i);
	rep(i,1,n) c[i]=read(); rep(i,1,m) d[i]=read();
	Down(i,n,1){
		for(auto t:pos[i]) st.insert(t);
		while(c[i]){
			auto it=st.upper_bound(a[i]);
			if(it==st.begin()) break; --it;
			int now=min(c[i],d[*it]); ans+=now;
			d[*it]-=now; c[i]-=now;
			if(d[*it]==0) st.erase(it);
		}
	} print(ans);
	return 0;
}
posted @   没学完四大礼包不改名  阅读(135)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示