牛客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大佬则只把文件输入关了一半,直接爆零(╯ ̄Д ̄)╯┻━┻ 要尽量 ...
随机推荐
- Android Studio 学习笔记(四):Adapter和RecyclerView说明
在现版本中,滚动控件有多种,而相比于ListView,GridView,RecyclerView的用途更广,因此将前两者作为Adapter适配器的引入,再对RecyclerView进行简单讲解. MV ...
- Edge Chromium 中如何始终允许运行 Flash 内容
众所周知,由于 Adobe Flash 控件历史久远,积累了许多漏洞.早在2017年7月,Adobe就宣布了要在2020年底终止对 Flash 的支持.微软称其浏览器移除 Flash 插件的最后期限是 ...
- LAN、WAN和WLAN的区别
1.LAN 局域网(Local Area Network)接口,通俗讲就是路由和用户之间网线口: 2.WAN 广域网(Wide Area Network),通俗讲就是和猫外部网连接的网线口: 3.WL ...
- 第2章 在 HTML中 使用 JavaScript
第2章 在 HTML中 使用 JavaScript 2.1 script 元素 2.1.1 标签的位置 2.1.2 延迟脚本 2.1.3 异步脚本 2.1.4 在XHTML中的使用 2.1.5 不推荐 ...
- 线段树区间染色 ZOJ 1610
Count the Colors ZOJ - 1610 传送门 线段树区间染色求染色的片段数 #include <cstdio> #include <iostream> #in ...
- 硬件知识整理part1--电阻E系列行业规范
仁者,人心也,义者,人路也. --孟子 “ E ”表示“指数间距”(Exponential Spacing) 在上个20世纪的电子管时代,电子元器件厂商为了便于元件规格的管理和选用.大规模生产的电 ...
- spss——定义变量
在变量视图 1,名称:必须以文字.字母.@ 这三类命名, 不能以数字.特殊字符.spss保留字等命名 2,类型:数字.逗号.点.字符串等,(字符数) 3,宽度 4,小数位数 5,标签:对名称进一步解释 ...
- oracle数据库应用开发经验
l 日志表应该以时间做分区,方便清理 一般应用都会有一些表用来记录用户操作日志,数据变更记录,交易流水等日志型的库表.这些表最好按时间字段做分区,这样在迁移或者清理历史记录时会比较方便,借助orac ...
- 转:Flutter开发中踩过的坑
记录一下入手Flutter后实际开发中踩过的一些坑,这些坑希望后来者踩的越少越好.本文章默认读者已经掌握Flutter初步开发基础. 坑1问题:在debug模式下,App启动第一个页面会很慢,甚至是黑 ...
- STL-map/multimap 简述
#include <iostream> #include <cstdio> #include <map> using namespace std; int main ...