核桃省选周赛 1.18 题解

比赛传送门:核桃省选周赛 1.18

打了 \(25+45+30=100\),加训 /fendou。

A 题解

想到了状态设计,转移写错了 /ll。

\(f_{i,0/1}\) 表示集合 \(i\) 涂了红/蓝颜色的最小代价。

枚举 \(i\) 的子集 \(j\),使 \(j\) 涂异色,那么所有在 \(i\) 的子集且不是 \(j\) 的子集的集合全部都要涂同色

\[f_{i,0}=\min_{j\subset i} \{f_{j,1} + sum(i,j,0) \} \]

\[f_{i,1}=\min_{j\subset i} \{f_{j,0} + sum(i,j,1) \} \]

其中 \(sum(i,j,0/1)\) 表示所有在 \(i\) 的子集且不是 \(j\) 的子集的集合涂红/蓝的代价,这里要特判一下 \(f_{i,0/1}\) 可以全涂一种颜色,这样做就是 \(O(3^n)\) 了。

然后考虑 \(sum(i,j,0/1)\) 本质在做什么,就是枚举 \(i-j\) 中为 \(1\) 的位置 \(x\),那么所有包含 \(x\) 且是 \(i\) 的子集都是涂这个颜色的,然后将 \(x\)\(i\) 中删去,重复这个过程。

因此我们 dp 转移时只需要枚举这个位置转移即可。

\[f_{i,0}=\min_{j\in i} \{\min\{f_{i-2^j,0},f_{i-2^j,1}\} + sum'(i,j,0) \} \]

\[f_{i,1}=\min_{j\in i} \{\min\{f_{i-2^j,0},f_{i-2^j,1}\} + sum'(i,j,1) \} \]

其中 \(sum'(i,j,0/1)\) 表示所有是 \(i\) 的子集且包含 \(j\) 位置的集合涂红/蓝的代价。

现在复杂度瓶颈在于 \(O(3^n)\) 的求子集的和,容易发现这就是一个高位前缀和,因为如果 \(i\)\(j\) 的子集,那么 \(i\) 的每一位都不超过 \(j\)

综上复杂度为 \(O(n2^n)\)

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N=1<<20;
int f[N][2],a[N],b[N],c[N],d[N];
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
main(){
//	freopen("color.in","r",stdin);
//	freopen("color.out","w",stdout);
	int n=read();
	for (int i=0;i<(1<<n);i++) a[i]=read();
	for (int i=0;i<(1<<n);i++) b[i]=read();
	for (int i=0;i<(1<<n);i++) c[i]=a[i],d[i]=b[i];
//	for (int i=0;i<(1<<n);i++) for (int j=i;j;j=(j-1)&i) c[i]+=a[j],d[i]+=b[j];
	for (int i=0;i<n;i++)
		for (int j=0;j<(1<<n);j++)
			if ((j>>i)&1) c[j]+=c[j^(1<<i)],d[j]+=d[j^(1<<i)];
	memset(f,0x3f,sizeof(f));
	for (int i=0;i<(1<<n);i++){
		f[i][0]=c[i],f[i][1]=d[i];
		for (int j=0;j<n;j++) if ((i>>j)&1){
			f[i][0]=min(f[i][0],min(f[i^(1<<j)][0],f[i^(1<<j)][1])+c[i]-c[i^(1<<j)]);
			f[i][1]=min(f[i][1],min(f[i^(1<<j)][0],f[i^(1<<j)][1])+d[i]-d[i^(1<<j)]);
		}
	}
	cout <<min(f[(1<<n)-1][0],f[(1<<n)-1][1]);
    return 0;
}

B 题解

假设 \(n,m\) 同阶。

首先先离散化。

考虑 \(m=1\) 怎么做,直接白雪皑皑 + 树状数组即可。

这启示我们根号分治,每个滤波器的询问相对独立,可以对每一个单独考虑。

若滤波器的询问个数大于 \(\sqrt n\) 则这样的滤波器肯定少于 \(\sqrt n\) 个,直接按照 \(m=1\) 做即可,但是我们需要平衡一下复杂度,使用 \(O(1)\) 修改 \(O(\sqrt n)\) 查询的分块即可。

若滤波器的询问个数小于 \(\sqrt n\),那么这个滤波器的允许范围最多只需要 \(O(\sqrt n)\) 个区间表示,这相当于一个二维数点,为了平衡复杂度使用 \(O(\sqrt n)\) 修改,\(O(1)\) 查询的分块即可。

维护这个区间可以使用 set,然后分讨一大堆情况。

但是这样空间似乎带了根号,我们只需要对于每一个滤波器一直使用一个数组并清空即可,代码一大坨,具体的可以看代码。

#include<bits/stdc++.h>
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
const int N=1e6+10,B=547; 
int n,m,q,a[N],f[N];vector<int>e;
inline int get(int x){return lower_bound(e.begin(),e.end(),x)-e.begin()+1;}
void print(int x){
	if (x>9) print(x/10);
	putchar(x%10^48);
}
int find(int x){if (f[x]==x) return x;return f[x]=find(f[x]);}
namespace baixueaiai{
	int c[N];vector<int>b[N];
	#define lowbit(x) x&-x
	inline void add(int i,int x){for (;i<=n;i+=lowbit(i)) c[i]+=x;}
	inline int sum(int i){int ans=0;for (;i;i-=lowbit(i)) ans+=c[i];return ans;}
	void main(){
		for (int i=1;i<=n+1;i++) f[i]=i;
		for (int i=1;i<=n;i++) b[a[i]].push_back(i); 
		while(q--){
			int op=read(),l=read(),r=read(),k=read();
			if (op==1){
				l=lower_bound(e.begin(),e.end(),l)-e.begin()+1;
				r=upper_bound(e.begin(),e.end(),r)-e.begin();
				if (l>r) continue;
				int x=l;
				while(x<=r){
					if (find(x)==x){
						for (auto i:b[x]) add(i,1);
						f[x]=x+1;
					}
					x=find(x);
				}
			}
			else print(sum(r)-sum(l-1)),putchar(10);
		}
	}
}
struct node{
	int op,l,r,k,id;
}b[N];
vector<int>mpb[N];
int anss[N];
unordered_map<int,vector<int>>mp; 
int bl[N],L[N],R[N],add[N],w[N],len,tot;
inline void build(){
	tot=n/len;if (n%len) tot++;
	for (int i=1;i<=n;i++) bl[i]=(i-1)/len+1;
	for (int i=1;i<=tot;i++) L[i]=(i-1)*len+1,R[i]=i*len;R[tot]=n;
}
inline void change(int l,int r,int x){
	if (bl[l]==bl[r]){
		for (int i=l;i<=r;i++) w[i]+=x;
		return ;
	}
	for (int i=l;i<=R[bl[l]];i++) w[i]+=x;
	for (int i=L[bl[r]];i<=r;i++) w[i]+=x;
	for (int i=bl[l]+1;i<bl[r];i++) add[i]+=x;
}
inline void change(int x,int y){w[x]+=y,add[bl[x]]+=y;}
inline int ask(int x){return w[x]+add[bl[x]];}
inline int ask(int l,int r){
	if (bl[l]==bl[r]){
		int ans=0;
		for (int i=l;i<=r;i++) ans+=w[i];
		return ans;	
	}
	int ans=0;
	for (int i=l;i<=R[bl[l]];i++) ans+=w[i];
	for (int i=L[bl[r]];i<=r;i++) ans+=w[i];
	for (int i=bl[l]+1;i<bl[r];i++) ans+=add[i];
	return ans;
}
inline void solve1(int x){
	for (int i=1;i<=n;i++) add[i]=w[i]=0;
	for (int i=1;i<=n+1;i++) f[i]=i;
	for (auto id:mp[x]){
		int op=b[id].op,l=b[id].l,r=b[id].r;
		if (op==1){
			l=lower_bound(e.begin(),e.end(),l)-e.begin()+1;
			r=upper_bound(e.begin(),e.end(),r)-e.begin();
			if (l>r) continue;
			int x=l;
			while(x<=r){
				if (find(x)==x){
					for (auto i:mpb[x]) change(i,1);
					f[x]=x+1;
				}
				x=find(x);
			}
		}
		else anss[id]=ask(l,r);
	}
}
#define pii pair<int,int>
set<pii>s;
struct query{
	int id,op,l,r;
};
vector<query>c[N];
int top;pii d[N];
inline void ins(int l,int r){d[++top]={l,r},s.insert({l,r});}
inline void del(int l,int r){for (int i=1;i<=tot;i++) if (d[i]==make_pair(l,r)){d[i]={0,0};break;}s.erase({l,r});}
inline void solve2(int x){
	s.clear();top=0;
	for (auto id:mp[x]){
		int op=b[id].op,l=b[id].l,r=b[id].r;
		if (op==1){
			l=lower_bound(e.begin(),e.end(),l)-e.begin()+1;
			r=upper_bound(e.begin(),e.end(),r)-e.begin();
			if (l>r) continue;
			if (s.empty()){ins(l,r);continue;}
			set<pii>::iterator it1=s.lower_bound({l,0}),it2=s.upper_bound({r,n+1}); 
			if (it1==s.begin()&&it2==s.begin()) ins(l,r);
			else if (it1==s.begin()&&it2!=s.begin()){
				it2--;
				r=max(r,(*it2).second);
				while(it1!=it2){
					set<pii>::iterator tmp=it1;
					it1++;
					del((*tmp).first,(*tmp).second);
				}
				if (!s.empty()) del((*it1).first,(*it1).second);
				ins(l,r);
			}
			else{
				if (it1==it2){
					it1--;
					if ((*it1).second<l) ins(l,r);
					else l=(*it1).first,r=max(r,(*it1).second),del((*it1).first,(*it1).second),ins(l,r);
				}
				else{
					it2--;
					r=max(r,(*it2).second);
					it1--;
					if ((*it1).second<l) it1++;
					else l=(*it1).first;
					while(it1!=it2){
						set<pii>::iterator tmp=it1;
						it1++;
						del((*tmp).first,(*tmp).second);
					}
					if (!s.empty()) del((*it1).first,(*it1).second);
					ins(l,r);
				}
			}
		}
		else{
			for (int i=1;i<=top;i++) if (d[i]!=make_pair(0,0)){
				if (l-1) c[l-1].push_back({id,-1,d[i].first,d[i].second});
				c[r].push_back({id,1,d[i].first,d[i].second});
			}
		}
	}
	for (int i=1;i<=top;i++) d[i]={0,0};
}
inline void solve(){
	for (int i=1;i<=n;i++) add[i]=w[i]=0;
	for (int i=1;i<=n;i++){
		change(a[i],n,1);
		for (auto j:c[i]){
			int id=j.id,op=j.op,l=j.l,r=j.r;
			anss[id]+=op*(ask(r)-ask(l-1));
		}
	}
}
main(){
//	freopen("filter.in","r",stdin); 
//	freopen("filter.out","w",stdout);
	n=read(),q=read(),m=read();
	for (int i=1;i<=n;i++) a[i]=read(),e.push_back(a[i]);
	sort(e.begin(),e.end());e.erase(unique(e.begin(),e.end()),e.end());
	for (int i=1;i<=n;i++) a[i]=get(a[i]);
	if (m==1){baixueaiai::main(); return 0;}
	len=sqrt(n);if (len==0) len=1;build();
	for (int i=1;i<=n;i++) mpb[a[i]].push_back(i);
	for (int i=1;i<=q;i++) b[i]={read(),read(),read(),read(),i},mp[b[i].k].push_back(i);
	for (auto i:mp) if (i.second.size()>B) solve1(i.first);else solve2(i.first);solve();
	for (int i=1;i<=q;i++) if (b[i].op==2) print(anss[i]),putchar(10);
    return 0;
}
posted @ 2026-01-19 12:44  OTn53_qwq  阅读(24)  评论(0)    收藏  举报