[2018HN省队集训D6T2] girls

时间:2023-02-06 12:11:54

[2018HN省队集训D6T2] girls

题意

给定一张 \(n\) 个点 \(m\) 条边的无向图, 求选三个不同结点并使它们两两不邻接的所有方案的权值和 \(\bmod 2^{64}\) 的值. 一个方案 \((i,j,k)\) 的权值定义为 \(iA+jB+kC\), 其中 \(A,B,C\) 给定且 \(i<j<k\). 点从 \(0\) 开始标号.

\(n,m\le 2\times 10^5\).

题解

出题人: 用心造题, 用脚造数据

这题场上看出标算, 然而其中 \(2\) 条边的贡献部分算挂了没调出来. 最后交了一个和 \(O(n^3)\) 裸暴力组起来的程序居然tm拿了 \(90\) 分...好气啊...感觉最后的努力都在和空气斗智斗勇实际上确实是, 因为式子是错的233

部分分里有一个 \(m=0\) 的, 由这个部分分可以想到如何快速求出所有选三个点的方案的贡献, 显然直接枚举结点计算出它贡献分别为 \(A,B,C\) 的方案数量就好了, 复杂度 \(O(n)\).

考虑容斥的解法. 计算出所有方案减去存在一条边的三元组再加上存在两条边的三元组再减去存在三条边的三元组. 注意为了保证容斥的正确性, 每个三元组的贡献应该是该三元组中对应结构的数量.

计算存在一条边的三元组贡献直接枚举边 \((u,v)\) 且 \(u<v\) , 则第三个点 \(w\) 与 \(u,v\) 的大小关系分三种情况讨论再用等差数列求和做就可以了. 这一步是 \(O(m)\) 的.

两条边的情况注意不能直接用三元环计数的方法做. 因为这样的结构的数量是 \(O(m^2)\) 的, 各种复杂度正确的枚举方法都枚举不全. 实际上我们可以枚举拐角处的结点是哪个, 然后预处理出和这个点邻接且编号大于/小于该点的结点分别有哪些, 丢到 std::vector 里排序前缀和一下就可以 \(O(m)\) 算了.

三条边是三元环计数问题. 按度数与 \(\sqrt m\) 的关系分类, 根据Handshake Lemma所有的点的度数和为 \(2m\), 所以度数大于 \(\sqrt m\) 的点不超过 \(\sqrt m\) 个, 直接枚举所有三元组计算即可. 这部分复杂度 \(O(m^{3/2})\). 度数小于 \(\sqrt m\) 的部分可以从每个点出发枚举两个出点, 判断这两个出点是否联通. 容易发现每条边最多被枚举 \(O(\sqrt m)\) 次, 这部分复杂度也是 \(O(m^{3/2})\) 的. 总复杂度 \(O(m^{3/2})\).

然而调了很久一直TLE. 为啥呢?

std::unordered_set 害人不浅...手写了一个十几行的Hash表判断联通性就过了...实测开 O2 之后查询时间要多 \(20\) 倍.

什么 \(O(1)\) 都是骗人的QAQ!

参考代码

#include <bits/stdc++.h>

const int MOD=1e6+37;
const int MAXV=2e5+10;
const int MAXE=1e6+10;
typedef unsigned long long uintEx; struct Edge{
int from;
int to;
Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* top=E; struct List{
uintEx key;
List* next;
List(const uintEx& k):key(k),next(NULL){}
};
List* L[MOD]; int v;
int e;
uintEx A;
uintEx B;
uintEx C;
int deg[MAXV];
std::vector<uintEx> le[MAXV];
std::vector<uintEx> gr[MAXV]; uintEx Sum(int,int);
void Insert(int,int);
bool Exist(const uintEx&);
void Insert(const uintEx&);
template<typename Int> void ReadInt(Int&);
template<typename Int,typename... Arg> void ReadInt(Int&,Arg&...); int main(){
ReadInt(v,e);
ReadInt(A,B,C);
for(int i=0;i<e;i++){
int a,b;
ReadInt(a,b);
++a,++b;
Insert(a,b);
Insert(b,a);
++deg[a],++deg[b];
}
uintEx ans=0;
std::vector<int> large,small;
int sqre=sqrt(e);
for(int i=1;i<=v;i++){
ans+=(uintEx(i-1)*uintEx(i-2)/2)*C*uintEx(i-1);
ans+=uintEx(i-1)*uintEx(v-i)*B*uintEx(i-1);
ans+=(uintEx(v-i)*uintEx(v-i-1)/2)*A*uintEx(i-1);
std::sort(le[i].begin(),le[i].end());
std::sort(gr[i].begin(),gr[i].end());
for(size_t k=1;k<le[i].size();k++)
le[i][k]+=le[i][k-1];
for(size_t k=1;k<gr[i].size();k++)
gr[i][k]+=gr[i][k-1];
if(deg[i]>sqre)
large.push_back(i);
else
small.push_back(i);
}
for(Edge* i=E;i!=top;i++){
if(i->from<i->to){
ans-=Sum(0,i->from-2)*A+uintEx(i->from-1)*(B*(i->from-1)+C*(i->to-1));
ans-=Sum(i->from,i->to-2)*B+uintEx(i->to-i->from-1)*(A*(i->from-1)+C*(i->to-1));
ans-=Sum(i->to,v-1)*C+uintEx(v-i->to)*(A*(i->from-1)+B*(i->to-1));
}
}
for(int i=1;i<=v;i++){
if(!le[i].empty()&&!gr[i].empty())
ans+=(*le[i].rbegin())*uintEx(gr[i].size())*A
+uintEx(le[i].size())*uintEx(gr[i].size())*B*(i-1)
+(*gr[i].rbegin())*uintEx(le[i].size())*C;
for(size_t k=1;k<le[i].size();k++)
ans+=le[i][k-1]*A+((le[i][k]-le[i][k-1])*B+(i-1)*C)*k;
for(size_t k=1;k<gr[i].size();k++)
ans+=gr[i][k-1]*B+((gr[i][k]-gr[i][k-1])*C+(i-1)*A)*k;
}
uintEx triple=0;
std::sort(large.begin(),large.end());
for(size_t ii=0;ii<large.size();ii++){
int i=large[ii];
for(size_t jj=ii+1;jj<large.size();jj++){
int j=large[jj];
if(!Exist((uintEx(i)<<32)|j))
continue;
for(size_t kk=jj+1;kk<large.size();kk++){
int k=large[kk];
if(Exist((uintEx(i)<<32)|k)&&Exist((uintEx(j)<<32)|k))
triple+=(i-1)*A+(j-1)*B+(k-1)*C;
}
}
}
int cnt=0;
for(auto s:small){
for(Edge* i=head[s];i!=NULL;i=i->next){
if(deg[i->to]<=sqre&&i->to<=s)
continue;
for(Edge* j=i->next;j!=NULL;j=j->next){
if(deg[j->to]<=sqre&&j->to<=s)
continue;
assert(++cnt<=2e7);
if(Exist((uintEx(i->to)<<32)|j->to)){
uintEx q[3]={s-1,i->to-1,j->to-1};
std::sort(q,q+3);
triple+=q[0]*A+q[1]*B+q[2]*C;
}
}
}
}
ans-=triple;
printf("%llu\n",ans);
return 0;
} inline uintEx Sum(int l,int r){
return l<=r?uintEx(l+r)*uintEx(r-l+1)/2:0;
} inline void Insert(int from,int to){
Insert((uintEx(from)<<32)|to);
if(to>from)
gr[from].push_back(to-1);
else
le[from].push_back(to-1);
top->from=from;
top->to=to;
top->next=head[from];
head[from]=top++;
} template<typename Int> void ReadInt(Int& target){
target=0;
register char ch=getchar();
while(!isdigit(ch))
ch=getchar();
while(isdigit(ch)){
target=target*10+ch-'0';
ch=getchar();
}
} template<typename Int,typename... Arg> void ReadInt(Int& target,Arg&... args){
ReadInt(target);
ReadInt(args...);
} inline void Insert(const uintEx& k){
int pos=k%MOD;
if(!L[pos])
L[pos]=new List(k);
else{
List* cur=L[pos];
while(cur->next&&cur->key!=k)
cur=cur->next;
if(cur->key!=k)
cur->next=new List(k);
}
} inline bool Exist(const uintEx& k){
for(List* cur=L[k%MOD];cur!=NULL;cur=cur->next){
if(cur->key==k)
return true;
}
return false;
}

[2018HN省队集训D6T2] girls

[2018HN省队集训D6T2] girls的更多相关文章

  1. &lbrack;2018HN省队集训D9T1&rsqb; circle

    [2018HN省队集训D9T1] circle 题意 给定一个 \(n\) 个点的竞赛图并在其中钦定了 \(k\) 个点, 数据保证删去钦定的 \(k\) 个点后这个图没有环. 问在不删去钦定的这 \ ...

  2. &lbrack;2018HN省队集训D8T1&rsqb; 杀毒软件

    [2018HN省队集训D8T1] 杀毒软件 题意 给定一个 \(m\) 个01串的字典以及一个长度为 \(n\) 的 01? 序列. 对这个序列进行 \(q\) 次操作, 修改某个位置的字符情况以及查 ...

  3. &lbrack;2018HN省队集训D8T3&rsqb; 水果拼盘

    [2018HN省队集训D8T3] 水果拼盘 题意 给定 \(n\) 个集合, 每个集合包含 \([1,m]\) 中的一些整数, 在这些集合中随机选取 \(k\) 个集合, 求这 \(k\) 个集合的并 ...

  4. &lbrack;Luogu P4143&rsqb; 采集矿石 &lbrack;2018HN省队集训D5T3&rsqb; 望乡台platform

    [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...

  5. &lbrack;2018HN省队集训D5T2&rsqb; party

    [2018HN省队集训D5T2] party 题意 给定一棵 \(n\) 个点以 \(1\) 为根的有根树, 每个点有一个 \([1,m]\) 的权值. 有 \(q\) 个查询, 每次给定一个大小为 ...

  6. &lbrack;2018HN省队集训D5T1&rsqb; 沼泽地marshland

    [2018HN省队集训D5T1] 沼泽地marshland 题意 给定一张 \(n\times n\) 的棋盘, 对于位置 \((x,y)\), 若 \(x+y\) 为奇数则可能有一个正权值. 你可以 ...

  7. &lbrack;Codeforces 321D&rsqb;&lbrack;2018HN省队集训D4T2&rsqb; Ciel and Flipboard

    [Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard 题意 给定一个 \(n\times n\) 的矩阵 \(A\), (\(n\) 为奇数) , ...

  8. &lbrack;2018HN省队集训D1T3&rsqb; Or

    [2018HN省队集训D1T3] Or 题意 给定 \(n\) 和 \(k\), 求长度为 \(n\) 的满足下列条件的数列的数量模 \(998244353\) 的值: 所有值在 \([1,2^k)\ ...

  9. &lbrack;2018HN省队集训D1T1&rsqb; Tree

    [2018HN省队集训D1T1] Tree 题意 给定一棵带点权树, 要求支持下面三种操作: 1 root 将 root 设为根. 2 u v d 将以 \(\operatorname{LCA} (u ...

随机推荐

  1. salt源码安装软件和yum安装软件

    上面简单列出了源码安装的sls文件书写思路. 涉及到一些固定的思路:如, 1,拷贝 解压安装时候需要依赖tar.gz存在 如果已安装则无需再次安装. 2,启动脚本 加入chk时候需要文件存在,如果已添 ...

  2. Android系统提供的开发常用的包名及作用

    android.app :提供高层的程序模型.提供基本的运行环境 android.content :包含各种的对设备上的数据进行访问和发布的类 android.database :通过内容提供者浏览和 ...

  3. 201521123115《java程序设计》第十一周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1. ...

  4. python安装selenium和下载浏览器驱动

    1.安装selenium     方法一:可以用在cmd中用pip命令安装. python默认自带pip工具,如果在电脑上配置了pip的环境变量,打开cmd命令窗口后可以直接输入命令pip insta ...

  5. 使用Chrome浏览器访问谷歌和*

    安装chrome插件 谷歌访问助手即可 就这么简单

  6. JVM运行时内存区域

    JVM运行java程序时会将内存划分为若干个不同的数据区域: (1)程序计数器: 1.占用内存空间不大. 2.程序计数器相当于JVM所执行的字节码(jvm指令)的“行号指示器”,通过程序计数器的“值” ...

  7. 深入理解JAVA I&sol;O系列三:字符流详解

    字符流为何存在 既然字节流提供了能够处理任何类型的输入/输出操作的功能,那为什么还要存在字符流呢?容我慢慢道来,字节流不能直接操作Unicode字符,因为一个字符有两个字节,字节流一次只能操作一个字节 ...

  8. 笔记本建立wifi热点的实用详细步骤

    准备工作: (1) 首先要打开开始菜单--->搜索“services.msc” 并打开(或者用win+R快捷键打开“运行”输入“service.msc”,点击确定)--->找到“WLAN ...

  9. HDUOJ-----Be the Winner

    此题用到的概念: [定义1]:若一堆中仅有一个石子,则被称为孤单堆.若大于1个,则称为充裕堆. [定义2]:T态中,若充裕堆的堆数大于等于2,则称为完全利他态,用T2表示:若充裕堆的堆数等于0,则称为 ...

  10. Nginx配置(需要把nginx的根目录指向ftp上传文件的目录。)

    改成