CF Round1642

D 想到了翻转前缀的做法,可惜没有写出来,果然还是太弱了……

%%% wly 构造神仙

今天上午补完了题,E 题意外的自己想出来了

ABC 不写了吧……

D 首先可以插入一个回文串 \(\texttt{abc} \dots \texttt{cba}\) ,如果插在前缀 \(\texttt{abc}\dots\) 后,那么前面的串可以抛下不管,效果相当于翻转前缀

结果乱胡出来一个操作次数 \(3n^2\) 的构造……直觉太差了……

事实上 \(\text{flip}\) 两次即可,看看代码吧

#include <cstdio>
#include <algorithm>
#include <vector>
#define xp first
#define yp second
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int _=503;
typedef pair<int,int> pii;
int a[_],p[_],o[_],n,cur;
pii b[_];
vector<pii> res;
vector<int> splt;
void flip(int t){
	if(t<2) return;
	for(int i=1;i<=t;++i)
		res.emplace_back(pii(cur+++t,b[p[i]].xp));
	cur+=t;
	splt.emplace_back(2*t);
	reverse(p+1,p+t+1);
	for(int i=1;i<=n;++i) o[p[i]]=i;
}
void solve(){
	n=read();cur=0;
	for(int i=1;i<=n;++i) b[i]=pii(a[i]=read(),i);
	sort(b+1,b+n+1);
	for(int i=1,t=1;i<=n;i=t){
		while(t<=n&&b[t].xp==b[i].xp) ++t;
		if((t-i)&1) {puts("-1");return;}
	}
	res.clear();splt.clear();
	for(int i=1;i<=n;++i) p[b[i].yp]=i;
	for(int i=1;i<=n;++i) o[p[i]]=i;
	for(int i=1;i<=n;++i){
   		int e=o[i];
		flip(e-1);flip(e);
   	}
	for(int i=1;i<=n;i+=2) splt.emplace_back(2);
	printf("%lu\n",res.size());
	for(pii e:res) printf("%d %d\n",e.xp,e.yp);
	printf("%lu\n",splt.size());
	for(int e:splt) printf("%d ",e);
	putchar('\n');
}
int main(){
	int T=read();
	while(T--) solve();
	return 0;
}

比想象中的好写一些,果然实现是关键

E 首先容易用并查集处理 0 区间的覆盖

考虑 1 区间发现只有被 0 区间覆盖得只剩一个数时才有贡献,我们要求出满足这个条件的时间点

那么离线下来记录每个位置被 0 区间覆盖的时间戳最小值,那么对每个满足条件的 1 区间区间求 \(\max\)

将这个时间点与自身出现的时间点取 \(\max\),那么就可以求出 1 区间最早的贡献在哪里,扫一遍求答案即可

#include <cstdio>
#include <vector>
#include <algorithm>
#pragma GCC optimize(2,3,"Ofast")
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
int n,q;
const int _=200003;
int nx[_],dn[18][_],tg[_],lg[_];
int nxt(int i){if(nx[i]==i) return i;return nx[i]=nxt(nx[i]);}
int qry(int l,int r){
	int k=lg[r-l+1];
	return max(dn[k][l],dn[k][r-(1<<k)+1]);
}
struct sta{int l,r,t;};
vector<sta> vt;
vector<int> vr[_];
int vx[_];
bool res[_];
int main(){
	n=read();q=read();
	for(int i=1;i<=n+1;++i) nx[i]=i;
	for(int i=1;i<=q;++i){
		int o=read();
		if(o) vx[i]=read();
		else{
			int l=read(),r=read(),t=read();
			if(t) vt.emplace_back((sta){l,r,i});
			else{
				int e=l;
				while(nxt(e)<=r)
					e=nxt(e),tg[e]=1,dn[0][e]=i,nx[e]=e+1;
			}
		}
	}
	for(int i=2;i<=n;++i) tg[i]+=tg[i-1],lg[i]=lg[i>>1]+1;
	for(int t=1;t<18;++t)
		for(int i=1;i+(1<<t)-1<=n;++i)
			dn[t][i]=max(dn[t-1][i],dn[t-1][i+(1<<(t-1))]);
	for(sta o:vt){
		if(tg[o.r]-tg[o.l-1]<o.r-o.l) continue;
		int x=nxt(o.l);
		vr[max(qry(o.l,o.r),o.t)].emplace_back(x);
	}
	for(int i=1;i<=q;++i){
		for(int x:vr[i]) res[x]=1;
		if(vx[i]){
			if(dn[0][vx[i]]&&dn[0][vx[i]]<=i){
				puts("NO");
				continue;
			}
			if(res[vx[i]]){
				puts("YES");
				continue;
			}
			puts("N/A");
		}
	}
	return 0;
}

F 一道妙题

假设 \(m=1\),那么很好做

我们不妨实现这样一个操作 \(f(i,S)\) 表示 \(i\) 数组与集合 \(S\) 中有没有数组不冲突

这个开个哈希表查询,动态维护集合 \(S\) 即可

此时我们就可以用双指针,对 \(w\) 排序后利用 \(f\) 对每一个 \(i\) 找到最小的满足 \(i,j\) 不冲突的 \(j\),由于 \(w_i\) 只会增大,那么 \(w_j\) 递减才有可能更优

现在数组大小可以到 5 ,考虑如何实现新的 \(f\)

我们发现之所以不能直接沿用 \(m=1\) 的做法,是因为同一对冲突的数组会被计算多次

考虑容斥 \(\sum_{i=1}^m {m\choose i}(-1)^i = -[m>0]\)

那么把一个数组所有子集 Hash 后再送进哈希表里,每个子集有一个 \((-1)^{|S|}\) 的权值,将权值求和后就可以判断 \(S\) 中到底有多少个数组和 \(i\) 冲突

#include <cstdio>
#include <unordered_map>
#include <algorithm>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int _=100003;
typedef unsigned long long ull;
const ull D=1000000007;
int a[_][5],w[_],p[_];
ull b[_][32];
int n,m;
unordered_map<ull,int> c;
bool cmp(const int i,const int j){return w[i]<w[j];}
void ins(int i){for(int s=1;s<(1<<m);++s) ++c[b[p[i]][s]];}
void del(int i){for(int s=1;s<(1<<m);++s) --c[b[p[i]][s]];}
int calc(int i){
	int r=0;
	for(int s=1;s<(1<<m);++s)
		if(__builtin_parity(s)) r+=c[b[p[i]][s]];
		else r-=c[b[p[i]][s]];
	return r;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;++i){
		for(int j=0;j<m;++j) a[i][j]=read();
		sort(a[i],a[i]+m);
		for(int s=1;s<(1<<m);++s){
			int pr=1;b[i][s]=0;
			for(int j=0;j<m;++j)
				if(s>>j&1){
					b[i][s]=b[i][s]*D+(a[i][j]-pr+1);
					pr=a[i][j];
				}
		}
		w[p[i]=i]=read();
	}
	sort(p+1,p+n+1,cmp);
	for(int i=1;i<=n;++i) ins(i);
	int mx=n,res=0x7fffffff;
	for(int i=1;i<=mx;++i){
		bool fl=0;
		del(i);
		while(calc(i)<mx-i) del(mx--),fl=1;
		if(fl) ins(++mx),res=min(res,w[p[i]]+w[p[mx]]);
	}
	if(res==0x7fffffff) puts("-1");
	else printf("%d\n",res);
	return 0;
}
posted @ 2022-02-24 22:00  yyyyxh  阅读(23)  评论(0编辑  收藏  举报