传送门

考场上觉得复杂度是假的就没怎么优化,然后考完题解帮我证明了它是真的……

首先合并可以用并查集维护,可以顺便维护出集合的大小

对于操作2,发现如果 \(size_i\) 是确定的,可以用权值线段树很方便的维护出合法的 \(size_j\)的个数

每次只需枚举出现过的 \(size_i\) 即可,所以我觉得复杂度是假的

  • 如果存在 \(\sum a_i = n\) ,那么有 \(diff \{a_i\} \leqslant \sqrt n\)

我们有 \(\sum size_i = n\) ,所以不同的size个数只有不超过根号个

所以用个unordered_set维护当前存在的size,线段树查询即可

yysy,我把线段树换成树状数组从1700ms变成了400ms

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define reg register int
//#define int long long char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
} int n, m;
int fa[N], cnt[N];
bool vis[N];
inline int find(int p) {return fa[p]==p?p:fa[p]=find(fa[p]);} namespace force{
void solve() {
for (int i=1; i<=n; ++i) fa[i]=i, cnt[i]=1, vis[i]=1;
ll ans;
for (int i=1,x,y,c,f1,f2; i<=m; ++i) {
if (read()&1) {
x=read(); y=read();
f1=find(x), f2=find(y);
if (f1!=f2) {
cnt[f1]+=cnt[f2];
vis[f2]=0;
fa[f2]=f1;
}
}
else {
ans=0;
c=read();
for (int i=1; i<=n; ++i) if (vis[i])
for (int j=i+1; j<=n; ++j) if (vis[j])
if (abs(cnt[i]-cnt[j])>=c) ++ans;
printf("%lld\n", ans);
}
}
exit(0);
}
} namespace task1{
int tl[N<<2], tr[N<<2], sum[N<<2];
#define tl(p) tl[p]
#define tr(p) tr[p]
#define sum(p) sum[p]
#define pushup(p) sum(p)=sum(p<<1)+sum(p<<1|1)
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r;
if (l==r) return ;
int mid=(l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
}
void upd(int p, int pos, int dat) {
if (tl(p)==tr(p)) {sum(p)+=dat; return ;}
int mid=(tl(p)+tr(p))>>1;
if (pos<=mid) upd(p<<1, pos, dat);
else upd(p<<1|1, pos, dat);
pushup(p);
}
int query(int p, int l, int r) {
if (l<=tl(p) && r>=tr(p)) return sum(p);
int mid=(tl(p)+tr(p))>>1, ans=0;
if (l<=mid) ans+=query(p<<1, l, r);
if (r>mid) ans+=query(p<<1|1, l, r);
return ans;
}
void solve() {
build(1, 1, n); upd(1, 1, n);
for (int i=1; i<=n; ++i) fa[i]=i, cnt[i]=1, vis[i]=1;
ll ans;
for (int i=1,x,y,c,f1,f2; i<=m; ++i) {
if (read()&1) {
x=read(); y=read();
f1=find(x), f2=find(y);
if (f1!=f2) {
upd(1, cnt[f1], -1); upd(1, cnt[f2], -1);
cnt[f1]+=cnt[f2];
upd(1, cnt[f1], 1);
vis[f2]=0;
fa[f2]=f1;
}
}
else {
ans=0;
c=read();
for (reg i=1,l,r; i<=n; ++i) if (vis[i]) {
if ((l=cnt[i]-c)>=1) ans+=query(1, 1, l)-(c==0);
if ((r=cnt[i]+c+(c==0))<=n) ans+=query(1, r, n);
}
printf("%lld\n", ans/2);
}
}
exit(0);
}
} namespace task2{
int tl[N<<2], tr[N<<2], sum[N<<2], tot[N], maxn=1;
#define tl(p) tl[p]
#define tr(p) tr[p]
#define sum(p) sum[p]
#define pushup(p) sum(p)=sum(p<<1)+sum(p<<1|1)
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r;
if (l==r) return ;
int mid=(l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
}
void upd(int p, int pos, int dat) {
if (tl(p)==tr(p)) {sum(p)+=dat; return ;}
int mid=(tl(p)+tr(p))>>1;
if (pos<=mid) upd(p<<1, pos, dat);
else upd(p<<1|1, pos, dat);
pushup(p);
}
int query(int p, int l, int r) {
if (l<=tl(p) && r>=tr(p)) return sum(p);
int mid=(tl(p)+tr(p))>>1, ans=0;
if (l<=mid) ans+=query(p<<1, l, r);
if (r>mid) ans+=query(p<<1|1, l, r);
return ans;
}
void solve() {
build(1, 1, n); upd(1, 1, n); tot[1]=n;
for (int i=1; i<=n; ++i) fa[i]=i, cnt[i]=1, vis[i]=1;
ll ans;
for (int i=1,x,y,c,f1,f2; i<=m; ++i) {
if (read()&1) {
x=read(); y=read();
f1=find(x), f2=find(y);
if (f1!=f2) {
upd(1, cnt[f1], -1); upd(1, cnt[f2], -1);
--tot[cnt[f1]]; --tot[cnt[f2]];
cnt[f1]+=cnt[f2];
++tot[cnt[f1]]; maxn=max(maxn, cnt[f1]);
upd(1, cnt[f1], 1);
vis[f2]=0;
fa[f2]=f1;
}
}
else {
ans=0;
c=read();
for (reg i=1,l,r; i<=maxn; ++i) if (tot[i]) {
if ((l=i-c)>=1) ans+=1ll*tot[i]*(query(1, 1, l)-(c==0));
//cout<<"l: "<<l<<' '<<tot[i]*(query(1, 1, l)-(c==0))<<endl;
if ((r=i+c+(c==0))<=n) ans+=1ll*tot[i]*query(1, r, n);
//cout<<"r: "<<r<<' '<<query(1, r, n)<<endl;
}
printf("%lld\n", ans/2);
}
}
exit(0);
}
} namespace task{
unordered_set<int> s;
unordered_set<int>::iterator sta[N];
int sum[N], tot[N], top;
inline void upd(int i, int dat) {for (; i<=n; i+=i&-i) sum[i]+=dat;}
inline int query(int i) {int ans=0; for (; i; i-=i&-i) ans+=sum[i]; return ans;}
void solve() {
upd(1, n); tot[1]=n; s.insert(1);
for (int i=1; i<=n; ++i) fa[i]=i, cnt[i]=1, vis[i]=1;
ll ans;
for (int i=1,x,y,c,f1,f2; i<=m; ++i) {
if (read()&1) {
x=read(); y=read();
f1=find(x), f2=find(y);
if (cnt[f1]<cnt[f2]) swap(f1, f2);
if (f1!=f2) {
upd(cnt[f1], -1); upd(cnt[f2], -1);
--tot[cnt[f1]]; --tot[cnt[f2]];
cnt[f1]+=cnt[f2];
if (++tot[cnt[f1]]==1) s.insert(cnt[f1]);
upd(cnt[f1], 1);
vis[f2]=0;
fa[f2]=f1;
}
}
else {
ans=0;
c=read();
int l, r;
for (unordered_set<int>::iterator it=s.begin(); it!=s.end(); ++it) {
//cout<<"*it: "<<*it<<endl;
if (!tot[*it]) {sta[++top]=it; continue;}
if ((l=*it-c)>=1) ans+=1ll*tot[*it]*(query(l)-(c==0));
//cout<<"l: "<<l<<' '<<tot[i]*(query(1, 1, l)-(c==0))<<endl;
if ((r=*it+c+(c==0))<=n) ans+=1ll*tot[*it]*(query(n)-query(r-1));
//cout<<"r: "<<r<<' '<<query(1, r, n)<<endl;
}
while (top) s.erase(sta[top--]);
printf("%lld\n", ans/2);
}
}
exit(0);
}
} signed main()
{
n=read(); m=read();
//if (n<=100) force::solve();
//else if (n<=1000) task1::solve();
//else task2::solve();
task::solve(); return 0;
}

题解 Dove 打扑克的更多相关文章

  1. 「10.28」Dove 打扑克(链表)·Cicada 与排序(概率)·Cicada 拿衣服(各种数据结构)

    A. Dove 打扑克 考场思考半天线段树树状数组,没有什么想法 打完暴力后突然想到此题用链表实现会很快. 因为只有$n$堆,所以设最多有$x$个不同的堆数,那么$x\times (x-1)/2==n ...

  2. [UPC10525]:Dove打扑克(暴力+模拟)

    题目描述 $Dove$和$Cicada$是好朋友,他们经常在一起打扑克来消遣时光,但是他们打的扑克有不同的玩法. 最开始时,牌桌上会有$n$个牌堆,每个牌堆有且仅有一张牌,第$i$个牌堆里里里那个扑克 ...

  3. NOIP 模拟 $36\; \rm Dove 打扑克$

    题解 \(by\;zj\varphi\) 引理 对于一个和为 \(n\) 的数列,不同的数的个数最多为 \(\sqrt n\) 证明: 一个有 \(n\) 个不同的数的数列,和最小就是 \(n\) 的 ...

  4. 20210811 Dove 打扑克,Cicada 与排序,Cicada 拿衣服

    考场 开考感觉 T3 比较可做.T1 看上去不难但毫无思路. 先想了 25min T3,想到一个确定左端点,二分最长的右端点,甚至想到了用猫树维护区间 or and...上厕所回来发现假了,没有单调性 ...

  5. 晚间测试13 A. Dove 打扑克 vector +模拟

    题目描述 分析 这道题比较关键的一点就是要看出最终牌数的种类数不会超过 \(\sqrt{n}\) 种 知道了这个性质我们就可以用 \(vector\) 维护一个有序的序列 \(vector\) 中存放 ...

  6. Noip模拟36 2021.8.11

    刚题的习惯还是改不了,怎么办??? T1 Dove打扑克 考场上打的动态开点线段树+并查集,考后发现自己像一个傻子,并查集就行.. 这几天恶补数据结构疯了 用树状数组维护后缀和,$siz_i$表示编号 ...

  7. csp-s模拟测试91

    csp-s模拟测试91 倒悬吃屎的一套题. $T1$认真(?)分析题意发现复杂度不能带$n$(?),计划直接维护答案,考虑操作对答案的影响,未果.突然发现可以动态开点权值线段树打部分分,后来$Tm$一 ...

  8. noip模拟36

    \(\color{white}{\mathbb{荷花映日,莲叶遮天,名之以:残荷}}\) 今天再次翻车掉出前十 开题看错 \(t1\) 以为操作2的值固定发现是个简单题,然后 \(t2\) 开始大力 ...

  9. 2021.8.11考试总结[NOIP模拟36]

    T1 Dove玩扑克 考场并查集加树状数组加桶期望$65pts$实际$80pts$,考后多开个数组记哪些数出现过,只扫出现过的数就切了.用$set$维护可以把被删没的数去掉,更快. $code:$ 1 ...

随机推荐

  1. NB-IoT物联网连接

    一.NB-1oT的专有能力物联网(Internet of Things).简称IoTNB-IoT就是指窄带物联网(Narrow Band-Internet of Things)技术目前关于NB-IoT ...

  2. C语言怎么实现可变参数

    可变参数 可变参数是指函数的参数的数据类型和数量都是不固定的. printf函数的参数就是可变的.这个函数的原型是:int printf(const char *format, ...). 用一段代码 ...

  3. sql语句优化(持续更新)

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引.2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引 ...

  4. javascript中“==”,“===”和“Object.is(a,b)”的区别

    作为两个量比较的三种方式"==","==="和"Object.is(a,b)"有一定区别,如下(具体见MDN): (1)Object.is( ...

  5. 鸿蒙轻内核定时器Swtmr:不受硬件和数量限制,满足用户需求

    摘要:本文通过分析鸿蒙轻内核定时器模块的源码,掌握定时器使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十四 软件定时器Swtmr>,作者:zhushy . 软件定时器(S ...

  6. SAS启动时自动执行代码

    有时候我们希望SAS启动时自动执行已经编写好的程序,可以按照以下方法实现: 首先正常打开SAS,编写我们想要让SAS启动时自动执行的代码,例如获取桌面文件夹路径,以便在其他程序中引用这个路径. pro ...

  7. Jenkins远程命令执行漏洞(CVE-2018-1000861)

    此漏洞没有回显,直接利用orange的exp执行命令反弹shell 工具地址https://github.com/orangetw/awesome-jenkins-rce-2019 web服务器下写1 ...

  8. Jenkins-CI 远程代码执行漏洞(CVE-2017-1000353)

    影响范围 所有Jenkins主版本均受到影响(包括<=2.56版本) 所有Jenkins LTS 均受到影响( 包括<=2.46.1版本) poc下载 https://github.com ...

  9. friend靶机

    仅供个人娱乐 靶机信息 https://www.vulnhub.com/entry/me-and-my-girlfriend-1,409/ 一.主机探测 二.信息收集 访问一下web站点,提示只能从本 ...

  10. 2020国防科大综述:3D点云深度学习——综述(3D点云分割部分)

    目录 摘要 1.引言: 2.背景 2.1 数据集 2.2评价指标 3.3D点云分割 3.1 3D语义分割 3.1.1 基于投影的方法 多视图表示 球形表示 3.1.2 基于离散的方法 稠密离散表示 稀 ...