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,3 时,答案是 7=3⊕4,取最大值。
数据范围
1≤n≤105;0<u,v≤n;0≤w<231。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
struct qwq{
int v;
int w;
int nxt;
}edge[2000001];
int head[2000001];
int cnt=-1;
void add(int u,int v,int w){
edge[++cnt].nxt=head[u];
edge[cnt].v=v;
edge[cnt].w=w;
head[u]=cnt;
}
int sum[2000001];
void dfs(int x,int fa){
for(int i=head[x];~i;i=edge[i].nxt){
int v=edge[i].v;
int w=edge[i].w;
if(v!=fa){
sum[v]=sum[x]^w;
dfs(v,x);
}
}
}
struct trie{
int ch[2];
}t[2000001];
int tot;
void build(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));
int n;
scanf("%d",&n);
for(int i=1;i<n;++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)build(sum[i],0);
int ans=0;
for(int i=1;i<=n;++i){
ans=max(ans,query(sum[i],0));
}
printf("%d\n",ans);
}
P5283 [十二省联考 2019] 异或粽子
题目描述
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 到 n。第 i 种馅儿具有一个非负整数的属性值 ai。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 k 个粽子。
小粽的做法是:选两个整数数 l, r,满足 1⩽l⩽r⩽n,将编号在 [l,r] 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子馅儿的属性值的异或和 。(异或就是我们常说的 xor 运算,即 C/C++ 中的 ˆ 运算符或 Pascal 中的 xor 运算符)
小粽想品尝不同口味的粽子,因此她不希望用同样的馅儿的集合做出一个以上的粽子。
小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
输入格式
第一行两个正整数 n, k,表示馅儿的数量,以及小粽打算做出的粽子的数量。
接下来一行为 n 个非负整数,第 i 个数为 ai,表示第 i 个粽子的属性值。 对于所有的输入数据都满足:1⩽n⩽5×105, 1⩽k⩽min{2n(n−1),2×105}, 0⩽ai⩽4294967295。
输出格式
输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
输入输出样例
输入 #1复制
3 2
1 2 3
输出 #1复制
6
说明/提示
| 测试点 | n | k |
|---|---|---|
| 1, 2, 3, 4, 5, 6, 7, 8 | ⩽103 | ⩽103 |
| 9, 10, 11, 12 | ⩽5×105 | ⩽103 |
| 13, 14, 15, 16 | ⩽103 | ⩽2×105 |
| 17, 18, 19, 20 | ⩽5×105 | ⩽2×105 |
实现代码:
cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=20000000+10;
struct Node{int id,rk;long long w;bool operator <(const Node &a)const{return w<a.w;}};
priority_queue<Node>q;
long long ans=1,x,s[N];
int a[N][2],size[N],n,m,tot;
void ins(long long x)
{
int u=0;
for(int i=31;i>=0;i--)
{
int ch=(x>>i)&1;size[u]++;
if(!a[u][ch])a[u][ch]=++tot;
u=a[u][ch];
}
size[u]++;
}
long long query(long long x,int rk)
{
int u=0;long long ans=0;
for(int i=31;i>=0;i--)
{
int ch=(x>>i)&1;
if(!a[u][ch^1])u=a[u][ch];
else if(rk<=size[a[u][ch^1]])u=a[u][ch^1],ans|=1LL<<i;
else rk-=size[a[u][ch^1]],u=a[u][ch];
}
return ans;
}
long long getin()
{
long long x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
return x;
}
int main()
{
n=getin(),m=getin();m<<=1;
for(int i=1;i<=n;i++)x=getin(),s[i]=s[i-1]^x;
for(int i=0;i<=n;i++)ins(s[i]);
for(int i=0;i<=n;i++)q.push((Node){i,1,query(s[i],1)});
for(int i=1;i<=m;i++)
{
Node t=q.top();ans+=t.w;q.pop();
if(t.rk<n)q.push((Node){t.id,t.rk+1,query(s[t.id],t.rk+1)});
}
cout<<(ans>>1)<<endl;
}
P1470 [IOI 1996 / USACO2.3] 最长前缀 Longest Prefix
题目描述
在生物学中,一些生物的结构是用包含其要素的大写字母序列来表示的。生物学家对于把长的序列分解成较短的序列(即元素)很感兴趣。
如果一个集合 P 中的元素可以串起来(元素可以重复使用)组成一个序列 S,那么我们认为序列 S 可以分解为 P 中的元素。元素不一定要全部出现(如下例中 BBC 就没有出现)。举个例子,序列 ABABACABAAB 可以分解为下面集合中的元素:{A,AB,BA,CA,BBC}。
序列 S 的前面 k 个字符称作 S 中长度为 k 的前缀。设计一个程序,输入一个元素集合以及一个大写字母序列,设 S′ 是序列 S 的前缀,使其可以分解为给出的集合 P 中的元素,求 S′ 的长度 k 的最大值。
输入格式
输入数据的开头包括若干个元素组成的集合 P,用连续的以空格分开的字符串表示。字母全部是大写,数据可能不止一行。元素集合结束的标志是一个只包含一个 . 的行,集合中的元素没有重复。
接着是大写字母序列 S,用字符串表示,每 76 个字符换一行。
输出格式
只有一行,输出一个整数,表示 S 符合条件的前缀的最大长度。
输入输出样例
输入 #1复制
A AB BA CA BBC
.
ABABACABAABC
输出 #1复制
11
说明/提示
【数据范围】
对于 100% 的数据,1≤card(P)≤200,1≤∣S∣≤2×105,P 中的元素长度均不超过 10。
翻译来自 NOCOW。
实现代码:
cpp
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
int dp[200005],m;
set<string> s[20];
int main(){
string tp;
while (cin>>tp){
if (tp==".") break;
s[tp.size()].insert(tp);
m=max(m,int(tp.size()));
}
int i,ans=0;
dp[0]=1;
string n;
n=" ";
while (cin>>tp){
n=n+tp;
}
for (i=1;i<n.size();i++){
for (int j=min(i,m);j>=1;j--){
string tt=n.substr(i-j+1,j);
if (s[tt.size()].count(tt)==1&&dp[i-j]==1){
ans=i;
dp[i]=1;
break;
}
}
}
cout<<ans;
}
CF25E Test
题目描述
给定 3 个字符串 s1,s2,s3,试求一个字符串,使 s1,s2,s3 都是这个字符串的子串,并使这个字符串最短。输出最短字符串的长度 l。
输入格式
第一行输入一个字符串,表示 s1。
第二行输入一个字符串,表示 s2。
第三行输入一个字符串,表示 s3。
输出格式
第一行输出一个正整数,表示答案 l。
显示翻译
题意翻译
输入输出样例
输入 #1复制
ab
bc
cd
输出 #1复制
4
输入 #2复制
abacaba
abaaba
x
输出 #2复制
11
说明/提示
1≤∣s1∣,∣s2∣,∣s3∣≤105。
实现代码:
cpp
#include <cstdio>
#include <cstring>
#define min(a,b) ((a)<(b))?(a):(b)
using namespace std;
const int INF=2147483646;
int nxt[4][100050],l[4];
char s[4][100050];
inline void Pre(){
for(int f=1;f<4;++f){
int p=0,len=l[f];
for(int i=2;i<=len;++i){
while(p&&s[f][i]!=s[f][p+1]) p=nxt[f][p];
if(s[f][i]==s[f][p+1]) ++p;
nxt[f][i]=p;
}
}
return ;
}
int f(int a,int b,int c){
int ans=INF,p1=0;
int al=l[a],bl=l[b],cl=l[c];
for(int i=1;i<=al;++i){
while(p1&&s[b][p1+1]!=s[a][i]) p1=nxt[b][p1];
if(s[b][p1+1]==s[a][i]) ++p1;
if(p1==bl){
p1=-1;
break;
}
}
if(p1==-1){
int p=0;
for(int i=1;i<=al;++i){
while(p&&s[c][p+1]!=s[a][i]) p=nxt[c][p];
if(s[c][p+1]==s[a][i]) ++p;
if(p==cl){
p=-1;
break;
}
}
if(p==-1) ans=al;
else ans=al+cl-p;
}
else{
int p=0;
for(int i=1;i<=al;++i){
while(p&&s[c][p+1]!=s[a][i]) p=nxt[c][p];
if(s[c][p+1]==s[a][i]) ++p;
if(p==cl){
p=-1;
break;
}
}
if(p!=-1){
int p2=p;
for(int i=p1+1;i<=bl;++i){
while(p2&&s[c][p2+1]!=s[b][i]) p2=nxt[c][p2];
if(s[c][p2+1]==s[b][i]) ++p2;
if(p2==cl){
p2=-1;
break;
}
}
if(p2==-1) ans=al+bl-p1;
else ans=al+bl-p1 + cl -p2;
}
else ans=al+bl-p1;
}
return ans;
}
int main(){
scanf("%s%s%s",s[1]+1,s[2]+1,s[3]+1);
int ans=INF;
l[1]=strlen(s[1]+1);l[2]=strlen(s[2]+1);l[3]=strlen(s[3]+1);
Pre();
ans=min(ans,f(1,2,3));ans=min(ans,f(1,3,2));
ans=min(ans,f(2,1,3));ans=min(ans,f(2,3,1));
ans=min(ans,f(3,2,1));ans=min(ans,f(3,1,2));
printf("%d",ans);
return 0;
}