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<=50000m<=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
",ans[i]);
}