8/3 训练日志 (树状数组+区间覆盖+思维+01字典树)
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8/3 训练日志 (树状数组+区间覆盖+思维+01字典树)相关的知识,希望对你有一定的参考价值。
P5149 会议座位
又看了一遍树状数组,通透了好多。
思路:
1.采用字典树统计记录单词原本的次序,再记录调换后的次序
2.采用树状数组求出逆序对。(本质只求正序对)
大小写敏感,26*2=52个字符,可能有52 个分支,最大5层
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
using namespace std;
const int N=5e5+5;
const int inf=0x3f3f3f3f;
int n,a[N];
int ch[N][55],idx,cnt[N],tree[N];
char s[N];
void ins(char *s,int rk)
int p=0;
for(int i=0;s[i];i++)
int j=s[i]-'a';
if(!ch[p][j]) ch[p][j]=++idx;
p=ch[p][j];
cnt[p]=rk;
int query(char *s)
int p=0;
for(int i=0;s[i];i++)
int j=s[i]-'a';
p=ch[p][j];
return cnt[p];
int lowbit(int x)return x&(-x);
void add(int x,int k)
for(;x<=n;x+=lowbit(x))
tree[x]+=k;
int qry(int x)
int tmp=0;
for(;x>=1;x-=lowbit(x))
tmp+=tree[x];
return tmp;
signed main()
std::cin.tie(nullptr);
std::ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>s;
ins(s,i);
for(int i=1;i<=n;i++)
cin>>s;
a[i]=query(s);
int ans=0;
for(int i=1;i<=n;i++)
add(a[i],1);
ans+=i-qry(a[i]);
cout<<ans<<endl;
return 0;
D. Color with Occurrences
思路:
1.思路并不复杂,代码要注意的细节较多。考察的知识点是贪心、区间覆盖问题。
2.先枚举各个字符串能匹配的区间,包含左右端点,隶属的id号。
3.在将区间排序。对于每个区间,找到相同起点能覆盖的最远的段。
具体可看代码理解,细节较多。
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
using namespace std;
const int N =1e6+100;
const int mod=998244353;
string t,s;
int n,cnt;
struct node
int l,r,id;
e[N];
bool cmp(node e1,node e2)
if(e1.l==e2.l) return e1.r<e2.r;
return e1.l<e2.l;
vector<pair<int,int>>ans;
signed main()
int q;cin>>q;
while(q--)
ans.clear();cnt=0;
cin>>t;cin>>n;
int len_t=t.length();
for(int i=1;i<=n;i++)
cin>>s;int len_s=s.length();
for(int j=0;j<len_t-len_s+1;j++)
int flag=1;
for(int k=0;k<len_s;k++)
if(s[k]!=t[k+j])
flag=0;break;
if(flag)
e[++cnt].l=j+1;
e[cnt].r=j+len_s-1+1;
e[cnt].id=i;
sort(e+1,e+cnt+1,cmp);
int now=1,l=-1,r=0,g,f1=1;
while(r<len_t)
l=r+1;
for(int i=now;i<=cnt;i++)
if(e[i].l<=l&&e[i].r>=l)
if(e[i].r>r)
r=e[i].r;g=i;
else if(e[i].l>l)
now=i;break;
if(r<l)
f1=0;break;
ans.push_back(e[g].id,e[g].l);
if(!f1)
cout<<-1<<endl;continue;
cout<<ans.size()<<endl;
for(int i=0;i<ans.size();i++)
cout<<ans[i].first<<" "<<ans[i].second<<endl;
return 0;
E. Choosing The Commander
题意:每个士兵都有一个个性值,给定一个长官的pi和li,使得x^pi<li,统计满足条件得士兵个数。
思路:
1.操作1和2,分表在字典树中添加一个数和删去一个数
2.预处理出每个数字经过各个idx的数量
3.分别枚举pi和li得二进制位,ans加上pi和x异或值为0的数量,再将p指向异或为1的方向,因为下面可能还会有满足条件的士兵。
ps:编号p指向0的边表示为ch[p][0]
,指向1的边表示为ch[p][1]
出现编号为0,要及时退出。这很重要
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
using namespace std;
const int N=3e6+5;
const int inf=0x3f3f3f3f;
int n,a[N];
int ch[N][2],idx,sum[N];
void ins(int x)
int p=0; //作为起始编号,无实际意义
for(int i=31;i>=0;i--)
int j=x>>i&1;
if(!ch[p][j]) ch[p][j]=++idx;
p=ch[p][j];
sum[p]++;
void del(int x)
int p=0;
for(int i=31;i>=0;i--)
int j=x>>i&1;
p=ch[p][j];
sum[p]--;
int query(int x,int y)
int p=0,res=0; //p在j的上一层
for(int i=31;i>=0;i--)
int a=x>>i&1;
int b=y>>i&1;
if(b)
res+=sum[ch[p][a]];
p=ch[p][1-a];
else
p=ch[p][a];
if(!p) break; //若不写会wa2
return res;
signed main()
std::cin.tie(nullptr);
std::ios::sync_with_stdio(false);
int q;cin>>q;
while(q--)
int g;cin>>g;
if(g==1)
int x;cin>>x;ins(x);
else if(g==2)
int x;cin>>x;del(x);
else
int x,y;cin>>x>>y;
cout<<query(x,y)<<endl;
return 0;
01字典树
经典问题:最大异或对
题意:给定一个数组,任选两个数进行异或,得到的最大数是多少
#include<bits/stdc++.h>
#define endl '\\n'
using namespace std;
const int N=3e5+5;
const int inf=0x3f3f3f3f;
int n,a[N];
int ch[N*31][2],idx;
void ins(int x)
int p=0; //作为起始编号,无实际意义
for(int i=30;i>=0;i--)
int j=x>>i&1;
if(!ch[p][j]) ch[p][j]=++idx;
p=ch[p][j];
int query(int x)
int p=0,res=0; //p在j的上一层
for(int i=30;i>=0;i--)
int j=x>>i&1;
if(ch[p][!j]) //走相反位
res+=1<<i;p=ch[p][!j];
else
p=ch[p][j];
if(!p) break; //若不写会wa2
return res;
int main()
std::cin.tie(nullptr);
std::ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],ins(a[i]);
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,query(a[i]));
cout<<ans<<endl;
return 0;
P4551 最长异或路径
性质:x ^y ^y=x
(一个数,如果它两次异或同一个数,那么它是不会有改变的)
因此,i~j的路径上的异或和,
就可以表示成 根到i的异或和 ^ 根到j的异或和
。
思路:
1.预处理出根到各个点的异或值。
2.对异或值建立01字典树
3.查询取出极大值。
#include<bits/stdc++.h>
#define endl '\\n'
using namespace std;
const int N=3e5+5;
const int inf=0x3f3f3f3f;
int n,a[N];
int ch[N*31][2],idx,head[N],cnt,sum[N];
struct node
int to,nxt,w;
e[N];
void add(int from,int to,int w)
e[++cnt].to=to;
e[cnt].nxt=head[from];
e[cnt].w=w;
head[from]=cnt;
void dfs(int u,int fa)
for(int i=head[u];i;i=e[i].nxt)
int v=e[i].to;
if(v!=fa)
sum[v]=sum[u]^e[i].w;
dfs(v,u);
void ins(int x)
int p=0; //作为起始编号,无实际意义
for(int i=30;i>=0;i--)
int j=x>>i&1;
if(!ch[p][j]) ch[p][j]=++idx;
p=ch[p][j];
int query(int x)
int p=0,res=0; //p在j的上一层
for(int i=30;i>=0;i--)
int j=x>>i&以上是关于8/3 训练日志 (树状数组+区间覆盖+思维+01字典树)的主要内容,如果未能解决你的问题,请参考以下文章
2018年全国多校算法寒假训练营练习比赛(第五场)-E情人节的电灯泡(二维树状数组单点更新+区间查询)