7.12总结

7.12总结

得分情况

估分:0+50+100

实际:0+30+100

第二题打炸了

T1

题目大意

神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个1到N之间的正整数来表示一种颜色。树上一共有N个苹果。每个苹果都被编了号码,号码为一个1到N之间的正整数。我们用0代表树根。只会有一个苹果直接连到树根。

有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为N的苹果出发,由树枝走到编号为N的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。

神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
n<=50000

m<=100000

比赛的时候猜到是把询问离线跑树上莫队

然而…

我不会树上莫队呀

GG

T2

题目大意

神犇最近闲来无事,于是就思考哲学,研究数字之美。在神犇看来,如果一个数的各位能够被分成两个集合,而且这两个集合里的数的和相等,那么这个数就是优美的(具体原因就只有神犇才知道了)。现在神犇在思考另一个问题,在区间[A,B]中有多少个数是优美的?这个问题对于神犇来说很简单,相信对于你来说也不难。

对于100%的数据,1<=A<=B<=10^9。

正解

由于A,B的范围只有10^9,所以估计不是常规的数位dp(反正我这题也不会dp)

容易发现,最多只有9个数字,要把它们分成两个和相等的集合。那么方案总数肯定是比较小的。所以我们可以先把这种选择两个集合的方案求出来,再用暴力或其他手段求这种方案在1~n的范围有多少种。

先考虑如何求合法的数字集

方法一

枚举一个数字集,设总和为sum,用背包判断是否能从从中选出若干个数使和为sum/2。

用挡板法可以算出全部数字集的个数应该为\(\sum_{i=1}^9{C_{10+i-1}^{10-1}}\)

不到10^5个,是可以接受的

方法二(我的方法)

显然可以折半搜索,找出和为s的数字集,两两组合,然后去重

然而我折半搜索打错了。

一开始以为两边的数字集最多只有5个数字

然而对于{1,1,1,1,1,1,1,1};{8}这种,最多可以有8个数字

那么求出合法数字集之后,若数字集大小小于n,就随便排列。若数字集大小等于n,可以先枚举开头几位防止越界,剩下的用排列公式算。

注意不能有前导零

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct kk
{
	int num[11];
};

int L,R,i,j,x,ans,s,k,cnt,sum;
kk a[500005];
int b[15],num[10][2];
int jc[10];
int l[100005][10],next[100005],last[105],tot;

void insert(int x)
{
	tot++;
	for (int i=0;i<=b[0];i++)
	l[tot][i]=b[i];
	next[tot]=last[x];
	last[x]=tot;
}

void dfs(int x,int last,int s)
{
	if ((x>8)||(s>45)) return;
	insert(s);
	for (int i=last;i<=9;i++)
	{
		b[++b[0]]=i;
		dfs(x+1,i,s+i);
		b[0]--;
	}
}

int dg(int x,int len,int islow,int begin)
{
	int i;
	if (islow)
	{
		len=len-x+1;
		int s;
		s=jc[len];
		for (i=1;i<=cnt;i++)
		s=s/jc[num[i][1]];
		return s;
	}
	if (x>len)
	{
		return 1;
	}
	else
	{
		int s=0;
		for (int i=1;i<=cnt;i++)
		{
			if ((num[i][1]!=0)&&((islow==1)||(num[i][0]<=b[x]))&&((begin)||(num[i][0]>=1)))
			{
				num[i][1]--;
				if (num[i][0]==b[x])
				{
					s+=dg(x+1,len,islow,1);
				}
				else
				{
					s+=dg(x+1,len,1,1);
				}
				num[i][1]++;
			}
		}
		return s;
	}
}

int js(int up,int len)
{
	int x=up,s,i;
	b[0]=0;
	while (x>0)
	{
		b[++b[0]]=x%10;
		x/=10;
	}
	if (len>b[0]) return 0;
	if (len<b[0])
	{
		if (num[1][0]==0)
		{
			s=jc[len-num[1][1]];
			for (int i=2;i<=cnt;i++)
			s=s/jc[num[i][1]];
			s=s*jc[len-1]/jc[num[1][1]]/jc[len-1-num[1][1]];
		}
		else
		{
			s=jc[len];
			for (int i=1;i<=cnt;i++)
			s=s/jc[num[i][1]];
		}
		return s;
	}
	for (i=1;i<=b[0]/2;i++)
	swap(b[i],b[b[0]-i+1]);
	s=dg(1,len,0,0);
	return s;
}

int comp(kk a,kk b)
{
	if (a.num[0]<b.num[0]) return 1;
	if (a.num[0]>b.num[0]) return 0;
	for (int i=1;i<=a.num[0];i++)
	{
		if (a.num[i]<b.num[i]) return 1;
		if (a.num[i]>b.num[i]) return 0;
	}
	return 0;
}

int issame(kk a,kk b)
{
	if (a.num[0]!=b.num[0]) return 0;
	for (int i=1;i<=a.num[0];i++)
	{
		if (a.num[i]!=b.num[i]) return 0;
	}
	return 1;
}

int main()
{
	freopen("read.in","r",stdin);
	jc[0]=1;
	for (i=1;i<=9;i++)
	jc[i]=jc[i-1]*i;
	scanf("%d%d",&L,&R);
	R=min(R,999999999);
	dfs(0,0,0);
	for (x=1;x<=45;x++)
	{
		for (i=last[x];i>=1;i=next[i])
		{
			for (j=last[x];j>=1;j=next[j])
			{
				if ((j>i)||(l[i][0]+l[j][0]>9)) continue;
				sum++;
				a[sum].num[0]=0;
				for (k=1;k<=l[i][0];k++)
				a[sum].num[++a[sum].num[0]]=l[i][k];
				for (k=1;k<=l[j][0];k++)
				a[sum].num[++a[sum].num[0]]=l[j][k];
				sort(a[sum].num+1,a[sum].num+1+a[sum].num[0]);
			}
		}
	}
	sort(a+1,a+1+sum,comp);
	for (i=1;i<=sum;i++)
	{
		if (issame(a[i],a[i-1])) continue;
		cnt=1;
		num[1][0]=a[i].num[1];
		num[1][1]=1;
		for (k=2;k<=a[i].num[0];k++)
		{
			if (a[i].num[k]!=a[i].num[k-1])
			{
				cnt++;
				num[cnt][0]=a[i].num[k];
				num[cnt][1]=1;
			}
			else
			{
				num[cnt][1]++;
			}
		}
		ans=ans+js(R,a[i].num[0]);
		ans=ans-js(L-1,a[i].num[0]);
	}
	printf("%d",ans);
}

水法

分块打表。

每10^6记录一次答案。

T3

题目大意

深绘里一直很讨厌雨天。

灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连

根拔起,以及田地里的粮食被弄得一片狼藉。

无奈的深绘里和村民们只好等待救济粮来维生。

不过救济粮的发放方式很特别。

首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择

两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

对于100% 的数据,1 <= n;m <= 100000; 1 <= a, b, x, y <= n; 1 <= z <= 10^9

一眼树上差分+线段树合并。

对于修改操作(x,y,z),把+z标记打在x,y上面,把-z标记打在lca和fa[lca]上面。

把每个节点的儿子的线段树合并起来就是这个节点的值。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct qy
{
	int ch[2],masum,ma;
};

int n,m,i,j,k;
int x,y,z,xx;
int l[400005],next[400005],last[100005],tot,depth[100005];
int son[200005],nextt[200005],lastt[200005],tott;
int fa[100005][20];
qy tree[5000005];
int root[100005],cnt;
int ans[100005];
int pool[5000005];

void insert(int x,int y)
{
	tot++;
	l[tot]=y;
	next[tot]=last[x];
	last[x]=tot;
}

void insertt(int x,int y)
{
	tott++;
	son[tott]=y;
	nextt[tott]=lastt[x];
	lastt[x]=tott;
}

void build(int x)
{
	for (int i=1;i<=19;i++)
	fa[x][i]=fa[fa[x][i-1]][i-1];
	for (int i=last[x];i>=1;i=next[i])
	{
		if (depth[l[i]]==0)
		{
			depth[l[i]]=depth[x]+1;
			fa[l[i]][0]=x;
			insertt(x,l[i]);
			build(l[i]);
		}
	}
}

int lca(int x,int y)
{
	if (depth[x]<depth[y]) swap(x,y);
	for (int i=19;i>=0;i--)
	{
		if (depth[fa[x][i]]>=depth[y])
		x=fa[x][i];
	}
	if (x==y) return x;
	for (int i=19;i>=0;i--)
	{
		if (fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}

int newnode()
{
	return pool[pool[0]--];
}

void recycle(int x)
{
	tree[x].ch[0]=tree[x].ch[1]=tree[x].ma=tree[x].masum=0;
	pool[++pool[0]]=x;
}

int merge(int k1,int k2,int l,int r)
{
	if ((k1==0)||(k2==0)) return k1+k2;
	int k3=newnode();
	if (l==r)
	{
		tree[k3].masum=tree[k1].masum+tree[k2].masum;
		tree[k3].ma=l;
	}
	else
	{
		int mid=(l+r)/2;
		tree[k3].ch[0]=merge(tree[k1].ch[0],tree[k2].ch[0],l,mid);
		tree[k3].ch[1]=merge(tree[k1].ch[1],tree[k2].ch[1],mid+1,r);
		if (tree[tree[k3].ch[0]].masum>=tree[tree[k3].ch[1]].masum)
		{
			tree[k3].masum=tree[tree[k3].ch[0]].masum;
			tree[k3].ma=tree[tree[k3].ch[0]].ma;
		}
		else
		{
			tree[k3].masum=tree[tree[k3].ch[1]].masum;
			tree[k3].ma=tree[tree[k3].ch[1]].ma;
		}
	}
	recycle(k1);
	recycle(k2);
	return k3;
}

void modify(int k,int l,int r,int x,int y)
{
	if (l==r)
	{
		tree[k].masum+=y;
		tree[k].ma=l;
	}
	else
	{
		int mid=(l+r)/2;
		if (x<=mid)
		{
			if (tree[k].ch[0]==0)
			tree[k].ch[0]=newnode();
			modify(tree[k].ch[0],l,mid,x,y);
		}
		else
		{
			if (tree[k].ch[1]==0)
			tree[k].ch[1]=newnode();
			modify(tree[k].ch[1],mid+1,r,x,y);
		}
		if (tree[tree[k].ch[0]].masum>=tree[tree[k].ch[1]].masum)
		{
			tree[k].masum=tree[tree[k].ch[0]].masum;
			tree[k].ma=tree[tree[k].ch[0]].ma;
		}
		else
		{
			tree[k].masum=tree[tree[k].ch[1]].masum;
			tree[k].ma=tree[tree[k].ch[1]].ma;
		}
	}
}

void dg(int x)
{
	for (int i=lastt[x];i>=1;i=nextt[i])
	{
		dg(son[i]);
	}
	root[x]=newnode();
	for (int i=lastt[x];i>=1;i=nextt[i])
	{
		root[x]=merge(root[x],root[son[i]],1,1000000000);
	}
	for (int i=last[x];i>=1;i=next[i])
	{
		if (l[i]>0)
		{
			modify(root[x],1,1000000000,l[i],1);
		}
		else
		{
			modify(root[x],1,1000000000,-l[i],-1);
		}
	}
	if (tree[root[x]].masum>0)
		ans[x]=tree[root[x]].ma;
	else
		ans[x]=0;
}

int read()
{
	int s=0;char ch=getchar();
	while ((ch<'0')||(ch>'9')) ch=getchar();
	while ((ch>='0')&&(ch<='9'))
	{
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s;
}

int main()
{
	freopen("read.in","r",stdin);
	freopen("write.out","w",stdout);
	for (i=1;i<=5000000;i++)
	{
		pool[i]=5000000-i+1;
	}
	pool[0]=5000000;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n-1;i++)
	{
		x=read();
		y=read();
		insert(x,y);
		insert(y,x);
	}
	depth[1]=1;
	build(1);
	tot=0;
	memset(last,0,sizeof(last));
	for (i=1;i<=m;i++)
	{
		x=read();
		y=read();
		z=read();
		xx=lca(x,y);
		insert(xx,-z);
		if (fa[xx][0]!=0)
		insert(fa[xx][0],-z);
		
		insert(x,z);//顺序不能换 
		insert(y,z);
	}
	dg(1);
	for (i=1;i<=n;i++)
	printf("%d\n",ans[i]);
}
posted @ 2019-07-12 19:50  leason_lyx  阅读(142)  评论(0编辑  收藏  举报