牛客CSP-S提高组赛前集训营4 赛后总结
复读数组
分成 3 种区间算答案:
- 一个块内的区间
- 两个块交界处,长度小于块长的区间
- 长度不小于块长的区间
对于第三种区间,容易发现每个区间的权值一样,只需要算出个数即可.
对于前两种空间,我的思路是:对于一个重复出现的元素,记第一次出现的这个元素贡献权值,然后讨论每一个数会给哪些区间贡献权值即可.
3年OI一场空
不开long long见祖宗
代码:
#include<bits/stdc++.h>
#define LL long long
#define int long long
const int SIZE=200005,Mod=1000000007;
int n,k,Raw[SIZE],A[SIZE],Tot;
int pre[SIZE],nex[SIZE],pos[SIZE];
bool mk[SIZE];
long long Ans=0;
LL Pow(LL B,int P)
{
LL x=1;
for(;P;P>>=1)
{
if(P&1)x=(x*B)%Mod;
B=(B*B)%Mod;
}
return x;
}
LL Inv(LL x)
{
return Pow(x,Mod-2);
}
LL Sigma(LL L,LL R)
{
return (L+R)%Mod*(R-L+1+Mod)%Mod*Inv(2)%Mod;
}
signed main()
{
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&A[i]);
Raw[i]=A[i];
}
std::sort(Raw+1,Raw+1+n);
Tot=std::unique(Raw+1,Raw+1+n)-(Raw+1);
/*-------------块内-------------*/
for(int i=1;i<=n;i++)
{
A[i]=std::lower_bound(Raw+1,Raw+1+Tot,A[i])-Raw;
if(!mk[A[i]])mk[A[i]]=1;
else
{
nex[pos[A[i]]]=i;
pre[i]=pos[A[i]];
}
pos[A[i]]=i;
}
for(int i=1;i<=n;i++)
if(nex[i]==0)
nex[i]=n+1;
for(int i=1;i<=n;i++)
{
Ans+=1LL*(i-pre[i])*(n+1-i);
Ans%=Mod;
}
Ans=(Ans-Tot+Mod)%Mod;
LL Rem=Ans;
Ans=Ans*k%Mod;
/*-------------长度不小于整块的区间-------------*/
LL num=Sigma(1,(n*k-n+1)%Mod);
Ans=(Ans+num*Tot)%Mod;
/*-------------块与块交界处-------------*/
if(k>1)
{
for(int i=n+1;i<=2*n;i++)
A[i]=A[i-n];
memset(pre,0,sizeof(pre));
memset(nex,0,sizeof(nex));
memset(mk,0,sizeof(mk));
memset(pos,0,sizeof(pos));
for(int i=1;i<=2*n;i++)
{
if(!mk[A[i]])mk[A[i]]=1;
else
{
nex[pos[A[i]]]=i;
pre[i]=pos[A[i]];
}
pos[A[i]]=i;
}
for(int i=1;i<=2*n;i++)
if(nex[i]==0)
nex[i]=2*n+1;
LL Tem=0;
for(int i=1;i<=2*n;i++)
{
Tem+=1LL*(i-pre[i])*(2*n+1-i);
Tem%=Mod;
}
Tem-=2*Rem;
Tem-=Sigma(1,n*2-n+1)*Tot;
Tem=((Tem%Mod)+Mod)%Mod;
Ans+=Tem*(k-1);
}
printf("%lld",Ans%Mod);
return 0;
}
路径计数机
考虑把问题转化为"给定树上若干条链,求有多少对链相交".
不妨令\(F(S)\)表示\(S\)这个链的集合中有多少对链相交.
这个问题可以根据树上两链相交,一定有一条链两端点的LCA在另一条链上这个性质来求解.
考虑集合\(A\)包含所有长度为\(p\)的链,集合\(B\)包含所有长度为\(q\)的链.那么答案为\(F(A\or B)-(F(A)+F(B))\)
树链剖分维护,时间复杂度\(O(n^2\log^2 n)\).
#include<bits/stdc++.h>
#define LL long long
#define int long long
const int SIZE=6005;
int In()
{
char ch=getchar();
int x=0;
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,p,q,head[SIZE],nex[SIZE],to[SIZE],Tot;
int son[SIZE],Siz[SIZE],Dep[SIZE],F[SIZE],ID[SIZE],Top[SIZE],new_node;
struct Tree
{
int sum[SIZE*4],Tag[SIZE*4];
#define LC(x) (x<<1)
#define RC(x) (x<<1|1)
#define Mid ((L+R)>>1)
void push_up(int x){sum[x]=sum[LC(x)]+sum[RC(x)];}
void Do(int x,int L,int R,int K)
{
sum[x]+=(R-L+1)*K;
Tag[x]+=K;
}
void push_down(int x,int L,int R)
{
if(Tag[x])
{
Do(LC(x),L,Mid,Tag[x]);
Do(RC(x),Mid+1,R,Tag[x]);
Tag[x]=0;
}
}
void Change(int x,int L,int R,int X,int Y,int K)
{
if(L>Y||R<X)return;
if(L>=X&&R<=Y)
{
Do(x,L,R,K);
return;
}
push_down(x,L,R);
Change(LC(x),L,Mid,X,Y,K);
Change(RC(x),Mid+1,R,X,Y,K);
push_up(x);
}
int Query(int x,int L,int R,int X,int Y)
{
if(L>Y||R<X)return 0;
if(L>=X&&R<=Y)return sum[x];
push_down(x,L,R);
return Query(LC(x),L,Mid,X,Y)+Query(RC(x),Mid+1,R,X,Y);
}
}T;
void Link(int u,int v)
{
nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;
nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;
}
void DFS1(int u)
{
Siz[u]=1;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(Dep[v])continue;
Dep[v]=Dep[u]+1;
F[v]=u;
DFS1(v);
Siz[u]+=Siz[v];
if(Siz[v]>Siz[son[u]])
son[u]=v;
}
}
void DFS2(int u,int TOP)
{
ID[u]=++new_node;
Top[u]=TOP;
if(Siz[u]==1)return;
DFS2(son[u],TOP);
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(v==F[u]||v==son[u])continue;
DFS2(v,v);
}
}
void mk(int u,int v,int K)
{
while(Top[u]!=Top[v])
{
if(Dep[Top[u]]<Dep[Top[v]])
std::swap(u,v);
T.Change(1,1,n,ID[Top[u]],ID[u],K);
u=F[Top[u]];
}
if(Dep[u]<Dep[v])
std::swap(u,v);
T.Change(1,1,n,ID[v],ID[u],K);
}
int sum(int u,int v)
{
int x=0;
while(Top[u]!=Top[v])
{
if(Dep[Top[u]]<Dep[Top[v]])
std::swap(u,v);
x+=T.Query(1,1,n,ID[Top[u]],ID[u]);
u=F[Top[u]];
}
if(Dep[u]<Dep[v])
std::swap(u,v);
x+=T.Query(1,1,n,ID[v],ID[u]);
return x;
}
int LCA(int u,int v)
{
while(Top[u]!=Top[v])
{
if(Dep[Top[u]]<Dep[Top[v]])
std::swap(u,v);
u=F[Top[u]];
}
if(Dep[u]>Dep[v])
std::swap(u,v);
return u;
}
int Dis(int u,int v,int L)
{
return Dep[u]+Dep[v]-2*Dep[L];
}
struct node
{
int u,v,L;
}G[9000005];
int A[9000005],B[9000005],C,C1,C2;
signed main()
{
scanf("%lld%lld%lld",&n,&p,&q);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&u,&v);
Link(u,v);
}
Dep[1]=1;
DFS1(1);
DFS2(1,1);
for(int u=1;u<=n;u++)
for(int v=1;v<u;v++)
{
int L=LCA(u,v);
G[++C]=(node){u,v,L};
if(Dis(u,v,L)==p)
{
A[++C1]=C;
}
if(Dis(u,v,L)==q)
{
B[++C2]=C;
}
}
LL Ans=0,Ans1=0,Ans2=0;
/**************************************/
for(int i=1;i<=C1;i++)
mk(G[A[i]].L,G[A[i]].L,1);
for(int i=1;i<=C1;i++)
Ans1+=sum(G[A[i]].u,G[A[i]].v);
for(int i=1;i<=n;i++)
{
int Tem=sum(i,i);
Ans1-=Tem*(Tem-1)/2;
}
for(int i=1;i<=C1;i++)
mk(G[A[i]].L,G[A[i]].L,-1);
/**************************************/
for(int i=1;i<=C2;i++)
mk(G[B[i]].L,G[B[i]].L,1);
for(int i=1;i<=C2;i++)
Ans2+=sum(G[B[i]].u,G[B[i]].v);
for(int i=1;i<=n;i++)
{
int Tem=sum(i,i);
Ans2-=Tem*(Tem-1)/2;
}
/**************************************/
for(int i=1;i<=C1;i++)
mk(G[A[i]].L,G[A[i]].L,1);
for(int i=1;i<=C1;i++)
Ans+=sum(G[A[i]].u,G[A[i]].v);
for(int i=1;i<=C2;i++)
Ans+=sum(G[B[i]].u,G[B[i]].v);
for(int i=1;i<=n;i++)
{
int Tem=sum(i,i);
Ans-=1LL*Tem*(Tem-1)/2;
}
Ans=Ans-Ans1-Ans2;
Ans=1LL*C1*C2-Ans;
Ans*=4;
printf("%lld",Ans);
return 0;
}
排列计数机
略.
牛客CSP-S提高组赛前集训营4 赛后总结的更多相关文章
- 牛客网CSP-S提高组赛前集训营Round4
牛客网CSP-S提高组赛前集训营 标签(空格分隔): 题解 算法 模拟赛 题目 描述 做法 \(BSOJ6377\) 求由\(n\)长度的数组复制\(k\)次的数组里每个连续子序列出现数字种类的和 对 ...
- 牛客CSP-S提高组赛前集训营3 赛后总结
货物收集 二分答案.复杂度\(O(n\log n)\). 货物分组 用费用提前计算的思想,考虑用一个新的箱子来装货物会发生什么. 显然费用会加上后面的所有货物的总重. \(60\)分的\(O(n^2) ...
- 牛客CSP-S提高组赛前集训营5 赛后总结
A.无形的博弈 心理题. 答案为\(2^n\),可感性理解结论的正确性. #include<bits/stdc++.h> #define LL long long const LL Mod ...
- 牛客CSP-S提高组赛前集训营2 赛后总结
比赛链接 A.服务器需求 维护每天需要的服务器数量的全局最大值(记为\(Max\))和总和(记为\(sum\)),那么答案为: \[max(Max,\lceil\dfrac{sum}{m}\rceil ...
- 牛客CSP-S提高组赛前集训营1
牛客CSP-S提高组赛前集训营1 比赛链接 官方题解 before:T1观察+结论题,T2树形Dp,可以换根或up&down,T3正解妙,转化为图上问题.题目质量不错,但数据太水了~. A-仓 ...
- 牛客CSP-S提高组赛前集训营3
A 货物收集 显然是一个二分答案的题. #include<iostream> #include<cstdio> #include<cstring> #include ...
- 牛客CSP-S提高组赛前集训营2 ———— 2019.10.31
比赛链接 期望得分:100+20+20 实际得分:40+20+30 awa cccc T1 :基于贪心的思路,然后开始爆搜(雾 那必然是会死的,好吧他就是死了 #include<iostrea ...
- 牛客CSP-S提高组赛前集训营1———2019.10.29 18:30 至 22:00
期望得分:100+0+10 实际得分:40+0+0 考炸了... T1:题目链接 究竟为什么会这样,,, 仔细研读我的丑代码 发现... 枯辽.... #include<cstdio> # ...
- 牛客CSP-S提高组赛前集训营2 T2沙漠点列
原题链接 算法不难,比赛的时候就和cyc大佬一起yy了正解,不过因为交的时候比较急(要回寝室惹),我有两数组开错大小直接爆到50,cyc大佬则只把文件输入关了一半,直接爆零(╯ ̄Д ̄)╯┻━┻ 要尽量 ...
随机推荐
- OpenCV3入门(十)图像轮廓
1.图像轮廓 1.1图像轮廓与API函数 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形,相对于边缘,轮廓是连续的,边缘并不全部连续.一般地,获取图像轮廓要经过下面几个步骤: 1) 读取 ...
- GO的方法值和方法表达式用法
手册上关于这块的解释感觉不是很详细清晰,经过几个示例自己总结了下这块的用法. 方法表达式:说简单点,其实就是方法对象赋值给变量. 这里有两种使用方式: 1)方法值:隐式调用, struct实例获取方法 ...
- go实现java虚拟机01
前段时间看了一本书,说的是用go语言实现java虚拟机,很有意思,于是就花了一段时间学习了一下go语言,虽然对go的底层理解不是很深,但是写代码还是可以的,就当做个读书笔记吧! 链接在这里,另外还有一 ...
- 07.JS对象-2
前言: 学习一门编程语言的基本步骤(01)了解背景知识(02)搭建开发环境(03)语法规范(04)常量和变量(05)数据类型(06)数据类型转换(07)运算符(08)逻辑结构(09)函数(10)对象1 ...
- 在服务器上搭建远端git仓库
推荐使用运行Liunx的机器 请获取root权限后进行下面操作 安装git # 检查是否安装了git如果有版本号就无需再安装 git -v # 安装git sudo apt-get install g ...
- Windwos应急响应和系统加固(1)——Windwos操作系统版本介绍
Windwos操作系统版本介绍 1. Micorsoft Windows XP ·Microsoft官方发布时间以及终止提供服务时间:2001.10.25-2014.4.8 产生漏洞:MS08 ...
- Linux_simpl shell-利用Shell脚本for循环输出系统中的用户及其Shell
[root@localhost ~]# vim user.sh 1 #!/bin/bash 2 for i in `cut -d ":" -f1 /etc/passwd`; 3 d ...
- PMP--1.2 PMBOK指南组成部分
图1.2.5 PMBOK指南关键组成部分在项目中的相互关系说明:项目生命周期中包含项目阶段,项目阶段结束之后就是阶段关口: 而项目管理过程和项目管理过程组以及项目管理知识领域都是为了项目生命周期服 ...
- java 开发中 dom4j的简单用法
Java中处理XML的方式有很多种,个人任务dom4j还是比较好用的.下面介绍以下简单的使用方法 先把import补充上 import org.dom4j.Document; import org.d ...
- 全面了解Java中的15种锁概念及机制!
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 1.公平锁 / 非公平锁 2.可重入锁 / 不可重入锁 3.独享锁 / 共享锁 4.互斥锁 / 读 ...