9.9模拟赛

usaco24openG2. Grass Segments

题意

有一些区间 \([l_i,r_i]\),现在每一个区间有一个值 \(k_i\)

对每一个区间询问:和这个区间重合了超过长度 \(k\) 的区间有多少个。

题解

我们考虑抽象化对重合的表达,设现在有两个区间 \([l_i,r_i]\),和 \([l_j,r_j]\),假如两个区间有交,那么一定满足以下条件:

\[r_i\ge l_j\\ l_i\le r_j \]

这时我们可以发现交集的大小是这个式子:

\[k_i \le \min(r_i,r_j)-\max(l_i,l_j) \]

相当于满足:

\[\begin{cases} k_i\le r_i-l_j \\ k_i \le r_j-l_i \\ k_i \le r_j-l_j\end{cases} \]

可以转化为:

\[\begin{cases} l_j \le r_i-k_i \\ r_j \ge l_i + k_i \\ r_j-l_j \ge k_i \end{cases} \]

这是一个典型的 cdq,直接做就可以了,别忘了离散化。

code:

#include<bits/stdc++.h>
#define cmpvv [&](int x,int y){return vv[x]==vv[y]?x<y:vv[x]<vv[y];}
#define cmpv2 [&](int x,int y){return v2[x]==v2[y]?x>y:v2[x]<v2[y];}
using namespace std;
const int N=1e6+10; 
int n,K,vv[N],v2[N],p[N];
struct dat{
    int l,r,k;
    void rd(){cin>>l>>r>>k;}
}d[N];
int b1[N],t1,b2[N],t2,b3[N],t3,ans[N];
void cdq3(int l,int r){
    if(l>=r)return;
    int md=l+r>>1,i,p,K;
    cdq3(l,md),cdq3(md+1,r);
    for(i=l,p=md+1,K=0;i<=md;++i){
        while(p<=r&&v2[b3[p]]<=v2[b3[i]])K+=b3[p++]>n;
        if(b3[i]<=n)ans[b3[i]]+=K;
    }
    inplace_merge(b3+l,b3+md+1,b3+r+1,cmpv2);
}
void cdq(int l,int r){
    if(l>=r)return;
    int md=l+r>>1,i;
    cdq(l,md),cdq(md+1,r);
    t1=t2=0;
    for(i=l;i<=md;++i)
        if(p[i]<=n)b1[++t1]=p[i];
    for(i=md+1;i<=r;++i)
        if(p[i]>n)b2[++t2]=p[i];
    if(t1&&t2){
        t3=t1+t2;
        merge(b1+1,b1+t1+1,b2+1,b2+t2+1,b3+1,cmpvv);
        cdq3(1,t3);
    }
    inplace_merge(p+l,p+md+1,p+r+1,cmpvv);
}
int main(){
    ios::sync_with_stdio(false),cin.tie(0);
    int i,j,k,l,r,x,y,z;
    cin>>n;
    for(x=1;x<=n;++x)d[x].rd();
    for(x=1;x<=n;++x)vv[x]=d[x].k,vv[x+n]=d[x].r-d[x].l;
    for(x=1;x<=n+n;++x)p[x]=x;
    sort(p+1,p+n+n+1,cmpvv);
    for(x=1;x<=n;++x){
        vv[x]=d[x].l+d[x].k,vv[x+n]=d[x].r;
        v2[x]=d[x].r-d[x].k,v2[x+n]=d[x].l;
    }
    cdq(1,n+n);
    for(x=1;x<=n;++x)printf("%d\n",ans[x]-1);
    return 0;
}

方法2

考虑对于一个区间 \([l_i,r_i]\),最少重叠长度为 \(k_i\),怎样的区间 \([l_j,r_j]\) 可以与前者产生贡献;首先 \(r_j-l_j\ge k_i\),在满足这个条件的情况下需要有 \(r_j\ge l_i+k_i\land l_j\le r_i-k_i\),这里 \(\land\) 表示合取,即 C++ 中的 \(\mathrm{and}\)。正难则反,考虑用长度 \(\ge k_i\) 的区间数量减去 \(r_j<l_i+k_i\) 以及 \(l_j>r_i-k_i\) 的区间数量和;容易得知后两部分是不交的,可以分开计算。

统计这个数量这是一个经典的二维数点问题。直接将待处理的数组按照 \(k\) 降序排序,复制一份按照 \(r-l\) 排序。扫前者的同时从后者里面把满足 \(r-l\ge k\) 的区间不断加入答案,用 __gnu_pbds::tree 插入元素、统计答案即可。可以参考代码实现。

放代码:

#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
typedef tuple<int,int,int,int> tpi;
int main(){
  ios::sync_with_stdio(false);
  int n,p=0; cin>>n;
  vector<tpi> a(n),b;
  for(int i=0;i<n;i++){
    auto &[l,r,k,x]=a[i]; cin>>l>>r>>k,x=i;
  }
  sort(a.begin(),a.end(),[](tpi x,tpi y){
    return get<2>(x)>get<2>(y);
  });
  b=a,sort(b.begin(),b.end(),[](tpi x,tpi y){
    return get<1>(x)-get<0>(x)>get<1>(y)-get<0>(y);
  }); // 两种排序
  tree<pii,null_type,greater<>,rb_tree_tag,tree_order_statistics_node_update> L;
  tree<pii,null_type,less<>,rb_tree_tag,tree_order_statistics_node_update> R;
  vector<int> s(n);
  for(auto [l,r,k,x]:a){
    while(p<n&&get<1>(b[p])-get<0>(b[p])>=k)
      L.insert(make_pair(get<0>(b[p]),p)),R.insert(make_pair(get<1>(b[p]),p)),p++;
    // 加入可能成为答案的元素
    s[x]=L.size()-R.order_of_key(make_pair(l+k,0))-L.order_of_key(make_pair(r-k,n));
    // 使用 order_of_key 查排名,进行统计
  }
  for(int i:s)cout<<i-1<<'\n';
  return 0;
}

usaco24openP1. Identity Theft

题意:

给一些 01 串,现在你每一次操作都可以往其中一个 01 串的摸末尾添加一个数字。问至少需要多少次操作才可以让任意一个串都不是其他串的前缀。

题解:

我们先建一颗 trie 出来,发现我们对一些现在不满足要求的串操作,可以选择把这个串 \(S\) 添加成两种可能。

  • 在子树中选择一个度数为 \(1\) 的点,把串 \(S\) 添加到这个点的空的儿子上,代价为 \(dep\) 差值 \(+1\)。而且此时同时创造了一个叶子。
  • 在子树中选择一个满足要求的串 \(X\),然后把这个串和 \(S\) 分别挂到原来 \(X\) 叶子上的两个空儿子上代价为 \(dep\) 差值 \(+2\)。而且此时同时创造了两个叶子。

显然我们贪心的选择所有代价中最小的。用堆来维护代价。合并的时候启发式合并即可。\(\Theta(\sum|s_i|\log N)\).

code:

#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rof(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int> Pair;
const int Maxn=1e6;
int n,ans,tot,t[Maxn+5][2],cnt[Maxn+5],dep[Maxn+5];
priority_queue<Pair,vector<Pair>,greater<Pair>> q[Maxn+5];
inline void Insert(){
    string s;cin>>s;int p=0;
	for(auto i:s){
        if(!t[p][i-'0']) t[p][i-'0']=++tot,dep[tot]=dep[p]+1;
        p=t[p][i-'0'];
    } cnt[p]++;
}
inline void dfs(int x){
    int a=t[x][0],b=t[x][1];
    if(a) dfs(a); if(b) dfs(b);
    if(!a && !b) q[x].emplace(dep[x]+2,1),cnt[x]--;
    else if(!a || !b) swap(q[x],q[a+b]),q[x].emplace(dep[x]+1,2);
    else{
        if(q[a].size()<q[b].size()) swap(a,b);
        swap(q[x],q[a]); while(!q[b].empty())
            q[x].push(q[b].top()),q[b].pop();
    }
    while(cnt[x]--){
        auto [k,id]=q[x].top(); q[x].pop(),ans+=(k-dep[x]);
        if(id==1) q[x].emplace(k+1,1),q[x].emplace(k+1,1);
        else q[x].emplace(k+2,1);
    }
}

int main(){
    cin>>n; For(i,1,n) Insert();
    dfs(0); cout<<ans<<endl;
    return 0;
}

posted @ 2025-09-09 21:54  NeeDna  阅读(12)  评论(0)    收藏  举报