2018.07.12【2018提高组】模拟C组

111 篇文章 0 订阅
51 篇文章 0 订阅

前言: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) O5!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[k2]+max(need[k2+1]more[k2],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[k2+1]+max(more[k2]need[k2+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]) 1in,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);
}

后续:不存在的!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值