1 Trie树(01字典树)
P2580 于是他错误的点名开始了
题目背景
XS中学化学竞赛组教练是一个酷爱炉石的人。 他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛 CON900)。
题目描述
这之后校长任命你为特派探员,每天记录他的点名。校长会提供化学竞赛学生的人数和名单,而你需要告诉校长他有没有点错名。(为什么不直接不让他玩炉石。)
输入输出格式
输入格式
第一行一个整数 (n),表示班上人数。
接下来 (n) 行,每行一个字符串表示其名字(互不相同,且只含小写字母,长度不超过 (50))。
第 (n+2) 行一个整数 (m),表示教练报的名字个数。
接下来 (m) 行,每行一个字符串表示教练报的名字(只含小写字母,且长度不超过 (50))。
输出格式
对于每个教练报的名字,输出一行。 如果该名字正确且是第一次出现,输出 OK
,如果该名字错误,输出 WRONG
,如果该名字正确但不是第一次出现,输出 REPEAT
。
输入输出样例
输入样例 #1
5
a
b
c
ad
acd
3
a
a
e
输出样例 #1
OK
REPEAT
WRONG
说明
- 对于 (40\%) 的数据,(nle 1000),(mle 2000)。
- 对于 (70\%) 的数据,(nle 10^4),(mle 2 imes 10^4)。
- 对于 (100\%) 的数据,(nle 10^4),(m≤10^5)。
题解
Trie树基本操作。add
函数是建树过程。树上操作?
代码
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int t,cnt;
int n,m;
int f[600005][105];
int cd[600005];
char x[52];
int vis[600005];
void add(char c[]){
t=0;
int len=strlen(c+1);
for(int i=1;i<=len;i++){
if(!f[t][c[i]-'a']) f[t][c[i]-'a']=++cnt;
t=f[t][c[i]-'a'];
}
cd[t]=1;
}
void check(char c[]){
t=0;
int len=strlen(c+1);
for(int i=1;i<=len;i++){
t=f[t][c[i]-'a'];
if(!t) {printf("WRONG
");return;}
}
if(!cd[t]){printf("WRONG
");return;}
if(!vis[t]&&cd[t]) {printf("OK
");vis[t]=1;return;}
else {printf("REPEAT
");return ;}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
memset(x,0,sizeof(x));
scanf("%s",x+1);
add(x);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
memset(x,0,sizeof(x));
scanf("%s",x+1);
check(x);
}
return 0;
}
2 P4551 最长异或路径
题目描述
给定一棵(n)个点的带权树,结点下标从(1)开始到(N)。寻找树中找两个结点,求最长的异或路径。 异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
输入输出格式
输入格式
第一行一个整数(N),表示点数。
接下来 (n-1) 行,给出 (u,v,w) ,分别表示树上的 (u) 点和 (v) 点有连边,边的权值是 (w)。
输出格式
一行,一个整数表示答案。
输入输出样例
输入样例 #1
4
1 2 3
2 3 4
2 4 6
输出样例 #1
7
说明
最长异或序列是1-2-3,答案是 7 (=3 ⊕ 4)
数据范围
(1le n le 100000;0 < u,v le n;0 le w < 2^{31})
题解
orz记得补充
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2000002;
int n;
int head[maxn];
struct node{
int v,w,next;
}edge[maxn];
int cnt;
void add(int u,int v,int w){
cnt++;
edge[cnt].w=w;
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
int sum[maxn];
void dfs(int x,int fa){
for(int i=head[x];i;i=edge[i].next){
int v=edge[i].v;
int w=edge[i].w;
if(v!=fa){
sum[v]=sum[x]^w;
dfs(v,x);
}
}
}
int tot;
struct trie{
int ch[2];
}t[maxn];
void add(int val,int x){
for(int i=(1<<30);i;i>>=1){
//取出二进制下这个数的当前位置
bool c=val&i;
if(!t[x].ch[c]) t[x].ch[c]=++tot;
x=t[x].ch[c];
}
}
int query(int val,int x){
int ans=0;
for(int i=(1<<30);i;i>>=1){
bool c=val&i;
if(t[x].ch[!c]){
ans+=i;
x=t[x].ch[!c];//如果这一位可以进行异或就沿着这一条路往下走
}
else x=t[x].ch[c];
}
return ans;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n-1;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs(1,-1);//预处理出每一个节点到根的异或和
for(int i=1;i<=n;i++) add(sum[i],0);
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,query(sum[i],0));//查询,取最大值
printf("%d",ans);
return 0;
}
3 归并排序求逆序对P1908
题解
代码
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
#define N 1000005
int r[N],a[N];
int n;
long long ans=0;
void merge_sort(int s,int t){
if(s==t) return ;
int mid=s+t>>1;
merge_sort(s,mid);
merge_sort(mid+1,t);
int i=s,j=mid+1,k=s;
while(i<=mid&&j<=t){
if(a[i]<=a[j]) r[k++]=a[i++];
else r[k++]=a[j++],ans+=(long long)mid-i+1;
}
while(i<=mid) r[k]=a[i],k++,i++;
while(j<=t) r[k]=a[j],k++,j++;
for(int i=s;i<=t;i++){
a[i]=r[i];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
merge_sort(1,n);
printf("%lld",ans);
return 0;
}