2020牛客多校第七场By Rynar
A.Social Distancing
思路:打表,首先当n为偶数时,可以得到答案为(nr)^2
//暴搜巨慢打表
#define sqr(x) (x)*(x)
int n,r,r1,r2,cnt,ct;
struct node{
int x,y;
}a[N];
bool check(int x,int y){
if (x*x+y*y<=r&&x*x+y*y>=r2)return 1;
return 0;
}
bool check1(int x,int y){
if (x*x+y*y<=r)return 1;
return 0;
}
int dis(node a,node b){
return sqr(a.x-b.x)+sqr(a.y-b.y);
}
int q[10];
int ans;
int dp[9][31];
void dfs(int n,int p){
if (!n){
int tot=0;
for (int i=1;i<ct;i++)
for (int j=i+1;j<=ct;j++)
tot+=dis(a[q[i]],a[q[j]]);
ans=max(ans,tot);
return;
}
for (int i=p;i>=1;i--){
q[n]=i;
dfs(n-1,i);
}
}
int main(){
int T,x,y;
for (int i=3;i<=7;i+=2){
for (int j=1;j<=30;j++){
r=sqr(j);
r2=max(sqr(j)-12,0);
if (i==7)r2=max(sqr(j)-11,0);
cnt=0;
for (int k=-j;k<=j;k++)
for (int p=-j;p<=j;p++)
if (check(k,p)){
a[++cnt].x=k,a[cnt].y=p;
}
ct=i;ans=0;
dfs(i,cnt);
dp[i][j]=ans;
//printf("%d %d %d\n",i,j,ans);
}
}
for (int i=2;i<=8;i+=2){
for (int j=1;j<=30;j++){
dp[i][j]=sqr(i*j);
}
}
printf("{");
for (int i=0;i<=8;i++){
printf("{");
for (int j=0;j<=30;j++){
printf("%d",dp[i][j]);
if (j!=30)printf(",");
}
if (i!=8)printf("},\n");
}
printf("}};\n");
return 0;
}
B.Mask Allocation
const int N=1e6+10;
const int mod=1e9+7;
int n,m,k;
int main(){
int T,x,y;
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&m);
if (n>m)swap(n,m);
queue<int>q;
while (n!=0&&m!=0){
for (int i=1;i<=m/n*n;i++){
q.push(n);
}
m=m%n;
swap(n,m);
}
printf("%d\n",q.size());
while (!q.empty()){
printf("%d ",q.front());
q.pop();
}
puts("");
}
return 0;
}
C.A National Pandemic
思路:树链剖分
typedef long long ll;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
#define int ll
int n,m,k;
int si[N],dep[N],fa[N],son[N];
int id[N],top[N],cnt=0;//si:子树大小,dep:深度,fa:父亲,son:重儿子,top:重链的父亲
int head[N],step=0;
int res=0;
struct edge{
int to,n;
}e[N<<2];
void add(int x,int y){
e[step].to=y;
e[step].n=head[x];
head[x]=step++;
}
void dfs1(int x,int ff,int depth){//处理dep,si,fa,son
dep[x]=depth;
si[x]=1;
fa[x]=ff;
int mx=-1;
for (int i=head[x];~i;i=e[i].n){
int v=e[i].to;
if (v==ff)continue;
dfs1(v,x,depth+1);
si[x]+=si[v];
if (si[v]>mx)mx=si[v],son[x]=v;
}
}
void dfs2(int x,int topf){//x当前节点,topf当前链的最顶端的节点
id[x]=++cnt;//标记每个点的新编号
top[x]=topf;
if(!son[x])return;//如果没有儿子则返回
dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
for(int i=head[x];~i;i=e[i].n){
int v=e[i].to;
if(v==fa[x]||v==son[x])continue;
dfs2(v,v);
}
}
struct node{
int val,lazy;
int l,r;
}tr[N<<2];
void pushup(int rt){
tr[rt].val=tr[rt<<1].val+tr[rt<<1|1].val;
}
void putdown(int rt){
if(tr[rt].lazy){
tr[rt<<1].lazy+=tr[rt].lazy;
tr[rt<<1].val+=(tr[rt<<1].r-tr[rt<<1].l+1)*tr[rt].lazy;
tr[rt<<1|1].lazy+=tr[rt].lazy;
tr[rt<<1|1].val+=(tr[rt<<1|1].r-tr[rt<<1|1].l+1)*tr[rt].lazy;
tr[rt].lazy=0;
}
}
void build(int l,int r,int rt){
tr[rt].l=l;
tr[rt].r=r;
tr[rt].lazy=0;
if (l==r){
tr[rt].val=0;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void update(int l,int r,int add,int rt){
if (l<=tr[rt].l&&r>=tr[rt].r){
tr[rt].lazy+=add;
tr[rt].val+=(tr[rt].r-tr[rt].l+1)*add;
return;
}
putdown(rt);
if(l<=tr[rt<<1].r)
update(l,r,add,rt<<1);
if(r>=tr[rt<<1|1].l)
update(l,r,add,rt<<1|1);
pushup(rt);
}
int query(int l,int r,int rt){
if(l==tr[rt].l&&r==tr[rt].r)
return tr[rt].val;
putdown(rt);
if(l>=tr[rt<<1|1].l)
return query(l,r,rt<<1|1);
else if(r<=tr[rt<<1].r)
return query(l,r,rt<<1);
else
return query(l,tr[rt<<1].r,rt<<1)+query(tr[rt<<1|1].l,r,rt<<1|1);
}
void updateRange(int x,int y,int k){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(id[top[x]],id[x],k,1);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
update(id[x],id[y],k,1);
}
int qRange(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
res=query(id[top[x]],id[x],1);
ans+=res;
x=fa[top[x]];
}
//在同一链上时
if(dep[x]>dep[y])swap(x,y);
res=query(id[x],id[y],1);
ans+=res;
return ans;
}
int ad[N],ans1,num;
signed main(){
int T,x,y;
scanf("%lld",&T);
while (T--){
num=ans1=step=cnt=0;
memset(head,-1,sizeof head);
memset(ad,0,sizeof ad);
memset(son,0,sizeof son);
scanf("%lld%lld",&n,&m);
for (int i=1;i<n;i++){
scanf("%lld%lld",&x,&y);
add(x,y);add(y,x);
}
dfs1(1,0,1);//把1当成根
dfs2(1,1);
build(1,n,1);
while(m--){
int c,x,y,z;
scanf("%lld",&c);
if(c==1){
scanf("%lld%lld",&x,&y);
num++;
ans1+=y-dep[x];
updateRange(1,x,2);
}
else if(c==2){
scanf("%lld",&x);
int v=ans1-num*dep[x]+qRange(1,x);
if(v>ad[x]) ad[x]=v;
}
else{//询问x的子树权值和
scanf("%lld",&x);
printf("%lld\n",ans1-num*dep[x]+qRange(1,x)-ad[x]);
}
}
}
return 0;
}
D.Fake News
int main(){
int T,n;
scanf("%d",&T);
while (T--){
scanf("%lld",&n);
if (n==1||n==24)puts("Fake news!");
else puts("Nobody knows it better than me!");
}
return 0;
}
E.NeoMole Synthesis
F.Tokens on the Tree
G.Topo Counting
H.Dividing
typedef long long ll;
const int mod=1e9+7;
int main(){
ll n,k;
scanf("%lld%lld",&n,&k);
ll ans=0;
for(ll l=1,r;l<=min(n,k);l=r+1){
r=min(n/(n/l),min(n,k));
ans+=(r-l+1)*(n/l);
}
for(ll l=1,r;l<=min(n-1,k);l=r+1){
r=min((n-1)/((n-1)/l),min(n-1,k));
ans+=(r-l+1)*((n-1)/l);
}
printf("%lld\n",((ans-(n-1)+k-1)%mod+mod)%mod);
return 0;
}
I.Valuable Forests
思路:由Cayley公式(可由prufer序列证明),n个点的带标号树个数为n^(n-2)
一个森林内部节点的度数平方和等于2*(长度为2的路径数+长度为3的路径数)//以下没用到
证明:首先度数平方和必然是偶数,因为奇数平方仍然是奇数,不改变原来奇偶性,度数和为2*(n-1)为偶数,度数平方和仍然是偶数
若一个节点,他的度数为k(有k个相邻点),则度数平方为k^2,他在3点中间可能数为kC2=k*(k-1)/2,其为两点中一点的可能数为kC1=k
由于长度为2的路径具有对称性,对于其相邻的点来说也有此边的计数,要/2,所以一个点 为3点中间 和 为两点中一点 所带来的收益为k*(k-1)/2+k/2=k^2/2
所以所有点的度数平方和为2*(长度为2的路径数+长度为3的路径数)
const int N=5000+10;
typedef long long ll;
int mod;
ll C[N][N],F[N][N];
ll f[N],g[N],h[N],dp[N];
void get_C(int maxn){
C[0][0]=1;
for(int i=1;i<=maxn;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
int main(){
int T,n;
scanf("%d %d",&T,&mod);
get_C(N-1);
F[0][0]=1;
for(int i=1;i<N;i++){//F[i][j]为i^j
F[i][0]=1;
for(int j=1;j<N;j++) F[i][j]=F[i][j-1]*i%mod;
}
f[0]=f[1]=1;g[0]=g[1]=1;
for (int i=2;i<N;i++){//f[i]为i^(i-2)表示n点构成的树的总可能数
f[i]=F[i][i-2];
}
for(int i=2;i<N;i++){//g[i]表示i个点构成的森林的总数
for(int j=0;j<i;j++)
g[i]=(g[i]+C[i-1][j]*f[j+1]%mod*g[i-1-j])%mod;
}
h[0]=h[1]=0;
for(int i=2;i<N;i++){//h[i]表示n点构成的所有树的度数平方和
for(int j=1;j<i;j++){
h[i]=(h[i]+j*j*C[i-2][j-1]%mod*F[i-1][i-j-1])%mod;
//prufer序列从i-2个位置中选j-1个位置放p,其他位置放其他的,由于prufer序列性质,度数为(个数+1),因为p可以取1-i,最后*i
}
h[i]=h[i]*i%mod;
}
dp[0]=dp[1]=0;
for(int i=2;i<N;i++){//dp[i]表示n点构成的所有森林的度数平方和
for(int j=0;j<i;j++){
dp[i]=(dp[i]+(g[i-j-1]*h[j+1]+dp[i-j-1]*f[j+1])%mod*C[i-1][j])%mod;
}
}
while(T--){
scanf("%d",&n);
printf("%d\n",dp[n]);
}
}
J.Pointer Analysis
禁止类似码农教程的网站爬取,抄袭博客内容。
https://www.cnblogs.com/rair/

浙公网安备 33010602011771号