前言:OTL(again)
JZOJ 4272 序章-弗兰德的秘密
题目
求两棵树的最大同构(自(tai)行(cao)百(shuai)科(le))
分析
树形dp,
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示第1棵树的用
i
i
i当根节点和第2棵树的用
j
j
j当根节点的最大同构
可得
f
[
i
]
[
j
]
=
max
(
f
[
i
.
s
o
n
]
[
j
.
s
o
n
]
+
1
)
f[i][j]=\max(f[i.son][j.son]+1)
f[i][j]=max(f[i.son][j.son]+1),叶子节点是1,最终答案输出
f
[
1
]
[
1
]
f[1][1]
f[1][1]
怎样找同构的子树(匹配),所以时间复杂度
O
(
5
!
∗
n
2
)
O(5!*n^2)
O(5!∗n2)
代码
#include <cstdio>
#include <cctype>
#define us unsigned short
using namespace std;
struct node{us y,next;}e1[1001],e2[1001]; bool v[1001]; int s,t;
us n,m,deg1[1001],deg2[1001],ls1[1001],ls2[1001],f[1001][1001],x,y,w;
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void dfs(int dep,int sum,int u){
if (dep>deg1[s]) {f[s][t]=(f[s][t]>sum)?f[s][t]:sum; return;}//最大值
dfs(dep+1,sum,e1[u].next); bool flag=0; //不匹配
for (int i=ls2[t];i;i=e2[i].next) if (!v[i])//没有匹配过
v[i]=flag=1,dfs(dep+1,sum+f[e1[u].y][e2[i].y],e1[u].next),v[i]=0;//匹配
if (!flag&&f[s][t]<sum) f[s][t]=sum;//没有匹配过
}
void dp(int x){
if (!deg1[x]){for (int i=1;i<=m;i++) f[x][i]=1; return;}//叶子节点
for (int i=ls1[x];i;i=e1[i].next) dp(e1[i].y);//找儿子
for (int i=1;i<=m;i++){
if (!deg2[i]) f[x][i]=1;//叶子节点
else s=x,t=i,dfs(1,0,ls1[x]),f[x][i]++;//找同构
}
}
int main(){
freopen("frand.in","r",stdin);
freopen("frand.out","w",stdout);
n=in(); m=in();
for (int i=1;i<n;i++) x=in(),y=in(),deg1[x]++,e1[i]=(node){y,ls1[x]},ls1[x]=i;
for (int i=1;i<m;i++) x=in(),y=in(),deg2[x]++,e2[i]=(node){y,ls2[x]},ls2[x]=i;
dp(1); return !printf("%d",f[1][1]);
}
JZOJ 4273 圣章-精灵使的魔法语
题目
问在一个由左括号和右括号的区间里,最少要在两边添上多少括号使括号匹配。
分析
线段树,本来想尝(zuo)试(si)打zkw线段树的,结果绝望了一个下午,用need表示左边需要多少个括号,more表示左边多余的括号(也就是右边需要的括号),
n
e
e
d
[
k
]
=
n
e
e
d
[
k
∗
2
]
+
m
a
x
(
n
e
e
d
[
k
∗
2
+
1
]
−
m
o
r
e
[
k
∗
2
]
,
0
)
;
need[k]=need[k*2]+max(need[k*2+1]-more[k*2],0);
need[k]=need[k∗2]+max(need[k∗2+1]−more[k∗2],0);
m
o
r
e
[
k
]
=
m
o
r
e
[
k
∗
2
+
1
]
+
m
a
x
(
m
o
r
e
[
k
∗
2
]
−
n
e
e
d
[
k
∗
2
+
1
]
,
0
)
;
more[k]=more[k*2+1]+max(more[k*2]-need[k*2+1],0);
more[k]=more[k∗2+1]+max(more[k∗2]−need[k∗2+1],0);
需要的括号就是它的左区间需要的孩子以及右区间需要的孩子与左区间多余的括号的差(取max,是因为左区间多余的括号不够),多余的括号就是它的右区间多余的括号以及左区间多余的括号与右区间需要的括号的差。
代码
#include <cstdio>
#include <cctype>
#define N 150000
using namespace std;
int n,m; unsigned short ne[N*9],mr[N*9]; char s[N+1];
int max(int a,int b){return (a>b)?a:b;}
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void build(int k,int l,int r){
if (l==r) {ne[k]=(s[l]==')'); mr[k]=(s[l]=='('); return;}
int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r);
ne[k]=ne[k<<1]+max(ne[k<<1|1]-mr[k<<1],0);
mr[k]=mr[k<<1|1]+max(mr[k<<1]-ne[k<<1|1],0);
}
void update(int k,int l,int r,int x){
if (x==l&&x==r) {ne[k]^=1; mr[k]^=1;return;}//取反
int mid=(l+r)>>1;
if (x<=mid) update(k<<1,l,mid,x);
else update(k<<1|1,mid+1,r,x);
ne[k]=ne[k<<1]+max(ne[k<<1|1]-mr[k<<1],0);
mr[k]=mr[k<<1|1]+max(mr[k<<1]-ne[k<<1|1],0);
}
void query(int k,int l,int r,int x,int y,int &ans1,int &ans2){//ans1和ans2
int a1=0,a2=0,a3=0,a4=0,mid=(l+r)>>1;
if (x==l&&y==r) {ans1=ne[k];ans2=mr[k];return;}//找到叶子节点
if (x<=mid&&y>mid){//区间在中间
query(k<<1,l,mid,x,mid,a1,a2);
query(k<<1|1,mid+1,r,mid+1,y,a3,a4);
ans1=a1+max(a3-a2,0); ans2=a4+max(a2-a3,0);
}
else if (y<=mid) query(k<<1,l,mid,x,y,ans1,ans2);//左边
else query(k<<1|1,mid+1,r,x,y,ans1,ans2); //右边
}
void print(int x){if (x>9) print(x/10); putchar(x%10+48);}//输出流
int main(){
freopen("elf.in","r",stdin);
freopen("elf.out","w",stdout);
n=in(); m=in(); scanf("%s",s+1); build(1,1,n);
while (m--){
char s1[7]; int x,y;
scanf("%s",s1); x=in();
if (s1[0]=='C') update(1,1,n,x);//修改
else{
y=in(); int ans1=0,ans2=0;
query(1,1,n,x,y,ans1,ans2);//查询
if (ans1) print(ans1); else putchar('0'); putchar(' ');
if (ans2) print(ans2); else putchar('0'); putchar('\n');
}
}
return 0;
}
JZOJ 4274 终章-剑之魂
题目
对于 1 ≤ i ≤ n , max ( a [ i ] 按 位 与 a [ j ] ) 1\leq i\leq n,\max(a[i]按位与a[j]) 1≤i≤n,max(a[i]按位与a[j])
分析
对于这次比赛最水的一道题,的确很水,j从高位枚举到低位,然后扫描1到n当a[i]的第j位是否为1且和当前答案按位与是当前答案,那么统计个数,当个数超过1时,累计答案。
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
int n,ans,a[1000001];
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int main(){
freopen("sword.in","r",stdin);
freopen("sword.out","w",stdout);
n=in();
for (int i=1;i<=n;i++) a[i]=in();
for (int j=30;j>=0;j--){
int cnt=0,w=1<<j;
for (int i=1;i<=n;i++) if ((a[i]&ans)==ans&&(a[i]&w)==w) cnt++;
if (cnt>1) ans+=w;
}
return !printf("%d",ans);
}