核桃省选周赛 1.18 题解
比赛传送门:核桃省选周赛 1.18。
打了 \(25+45+30=100\),加训 /fendou。
A 题解
想到了状态设计,转移写错了 /ll。
设 \(f_{i,0/1}\) 表示集合 \(i\) 涂了红/蓝颜色的最小代价。
枚举 \(i\) 的子集 \(j\),使 \(j\) 涂异色,那么所有在 \(i\) 的子集且不是 \(j\) 的子集的集合全部都要涂同色。
其中 \(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 转移时只需要枚举这个位置转移即可。
其中 \(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;
}

浙公网安备 33010602011771号