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&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++)
{
int u,v,w;cin>>u>>v>>w;
add(u,v,w),add(v,u,w);
}
dfs(1,0);
for(int i=1;i<=n;i++)
ins(sum[i]);
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,query(sum[i]));
cout<<ans<<endl;
return 0;
}