文章目录
前言
毒瘤比赛,三紫二黑(而且感觉G完全有黑的难度)
也受到了下午统练的影响,做了一天qwq
AB很水,CD不错,EFG直接贺
也与我
A奇水
B是可以五分钟做一做的简单构造
C是小清新dp,可以斜优我不想动脑直接分治挂log
D思路不太难想,但写起来是真的恶心
E是标准CF那种令人哭笑不得的“…时…,所以只需要考虑”题,但是dp思想还是不错的
F是诡异的组合题,先找到答案上界再试图证明可以达到
G需要一个前置的小结论,然而我并不会。。。后面线段树快速合并区间的地方也不是很好想
CF643A Bear and Colors
Description \text{Description} Description
Limak 有 n n n 个球,从左到右编号依次为 1 … n 1 \dots n 1…n。同时又有 n n n 种颜色,从编号依次为 1 … n 1 \dots n 1…n。第 i i i 个球的编号为 t i t_i ti。
对于球中每一个固定的段(含有连续元素的集合),可以定义一个主要颜色,即此段中出现次数最多的颜色。在可以有多种主要颜色的情况下,选择编号最小的。
现有 n ( n + 1 ) 2 \dfrac{n(n + 1)}{2} 2n(n+1) 个不为空的段。对于每个颜色,你需要输出此颜色作为主要颜色的次数。
n ≤ 5000 n\le 5000 n≤5000
Solution \text{Solution} Solution
本来还觉得可能得想想,看到数据范围笑了。
直接开桶
n
2
n^2
n2 暴力扫一遍即可。
Description \text{Description} Description
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=5050;
#define ll long long
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m;
int bac[N],a[N],res,ans[N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int l=1;l<=n;l++){
memset(bac,0,sizeof(bac));
for(int r=l;r<=n;r++){
int o=a[r];
++bac[o];
if(bac[o]>bac[res]||(bac[o]==bac[res]&&o<res)) res=o;
ans[res]++;
}
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
/*
*/
CF643B Bear and Two Paths
Description \text{Description} Description
一共有
n
n
n 个结点,给出互异的四个点
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d,请你构造一个边数不超过
k
k
k 的无向图,使得
a
a
a 到
b
b
b、
c
c
c 到
d
d
d 之间都存在一条哈密尔顿路径,且
a
,
b
a,b
a,b 之间和
c
,
d
c,d
c,d 之间不能直接连通。
请输出你构造的路径。
4
≤
n
≤
1000
4\le n\le 1000
4≤n≤1000
Solution \text{Solution} Solution
首先,对于
n
=
4
n=4
n=4 的情况,显然无解。
否则,只要找到一种用边最少的方案即可。
手玩发现,可以通过构造
a
→
c
→
x
→
.
.
.
→
y
→
d
→
b
a\to c\to x\to...\to y\to d\to b
a→c→x→...→y→d→b,再加两条
a
→
x
a\to x
a→x 和
y
→
b
y\to b
y→b 的边满足要求,总边数是
n
+
1
n+1
n+1。
由于从
c
c
c 走一定要去往
a
,
b
a,b
a,b,这都需要加边跳,所以至少加两条边,答案不会少于这个了。
问题得以解决。
Code \text{Code} Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=5050;
#define ll long long
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m;
int x[N],a,b,c,d,vis[N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();m=read();
a=read();b=read();c=read();d=read();
if(n<=4||m<n+1){
printf("-1");return 0;
}
x[1]=a;x[n]=b;x[2]=c;x[n-1]=d;
vis[a]=vis[b]=vis[c]=vis[d]=1;
int tot=2;
for(int i=1;i<=n;i++){
if(!vis[i]) x[++tot]=i;
}
for(int i=1;i<=n;i++) printf("%d ",x[i]);
putchar('\n');
printf("%d %d ",c,a);
for(int i=3;i<=n-2;i++) printf("%d ",x[i]);
printf("%d %d\n",b,d);
return 0;
}
/*
*/
CF643C Levels and Regions
Description \text{Description} Description
有一种电子游戏,它由 n n n 个关卡组成,每个关卡都被赋予了一个值 t i t_i ti。
现在,你要将这些关卡分成 k k k 个级别,每个级别 j j j 对应了一段连续的关卡 [ l j , r j ] [l_j,r_j] [lj,rj],且必有 l j ≤ r j l_j\leq r_j lj≤rj。任何一个关卡在且仅在一个级别中。
然后,一名玩家将会从第 1 1 1 个关卡,按顺序一直刷到第 n n n 个关卡。当他打到第 i i i 个关卡时,设这个关卡所在的级别是 j j j,则他有 t i ∑ x = l j i t x \dfrac{t_i}{\sum\limits_{x=l_j}^{i}t_x} x=lj∑itxti 的概率在 1 1 1小时内AC这个关卡,然后进入下一关;或者没有 AC 这个关卡(但仍然花了 1 1 1 小时),还得再次挑战这个关卡。
你需要找到一种划分方法,使得该玩家期望 AK 该游戏的期望时间最小。输出这个最小的期望时间。
Solution \text{Solution} Solution
一道乍看很吓人但是仔细想想并不难的小清新 dp 题。
有一个期望相关的常用结论:若一件事做成的概率是
p
p
p,那么做成这件事需要的期望次数是
1
p
\dfrac{1}{p}
p1。
证明:设期望次数为
x
x
x,讨论第一次做成或者没做成,就有:
x
=
(
p
×
0
+
(
1
−
p
)
×
x
)
+
1
x=(p\times0+(1-p)\times x)+1
x=(p×0+(1−p)×x)+1
移项即可得。
回到本题。
显然不同段之间互相独立。
设
s
u
m
i
=
∑
j
=
1
i
t
j
sum_i=\sum_{j=1}^i t_j
sumi=∑j=1itj。
那么本题
(
l
,
r
)
(l,r)
(l,r) 区间划分成一段完成的期望次数就是:
∑
i
=
l
r
s
u
m
i
−
s
u
m
l
−
1
t
i
\sum_{i=l}^r \frac{sum_i-sum_{l-1}}{t_i}
i=l∑rtisumi−suml−1
也就是:
∑
i
=
l
r
s
u
m
i
t
i
−
s
u
m
l
−
1
×
∑
i
=
l
r
1
t
i
\sum_{i=l}^r \frac{sum_i}{t_i}-sum_{l-1}\times\sum_{i=l}^r \frac{1}{t_i}
i=l∑rtisumi−suml−1×i=l∑rti1
对
∑
i
=
l
r
s
u
m
i
t
i
\sum_{i=l}^r \dfrac{sum_i}{t_i}
∑i=lrtisumi 和
∑
i
=
l
r
1
t
i
\sum_{i=l}^r \dfrac{1}{t_i}
∑i=lrti1 分别求前缀和,我们就可以
O
(
1
)
O(1)
O(1) 转移了。
然后直观感受可以发现,这个东西是满足决策单调性的。
感性理解一下就是要使全局尽可能小,前面的
∑
i
=
l
r
s
u
m
i
t
i
\sum_{i=l}^r \dfrac{sum_i}{t_i}
∑i=lrtisumi 全合起来后是定值,后面多一个元素的时候,
∑
i
=
l
r
1
t
i
\sum_{i=l}^r \dfrac{1}{t_i}
∑i=lrti1 变大,那么为了多减一些,
s
u
m
l
−
1
sum_{l-1}
suml−1 的值应该相应的变大(或者至少不应该变小),所以转移点有单调性的。
直接上分治即可,时间复杂度
O
(
n
k
log
n
)
O(nk\log n)
O(nklogn)。
(看题解的大佬这题也可以斜优做把 log 去掉)
Code \text{Code} Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
#define ll long long
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m;
double sum[N],t[N];
double s1[N],s2[N];
inline double calc(int l,int r){
return (s1[r]-s1[l-1])-sum[l-1]*(s2[r]-s2[l-1]);
}
double dp[52][N];
void solve(int k,int l,int r,int tl,int tr){
if(l>r) return;
int mid=(l+r)>>1,pl(0);
for(int i=tl;i<=min(mid-1,tr);i++){
double w=dp[k-1][i]+calc(i+1,mid);
if(w<dp[k][mid]){
dp[k][mid]=w;pl=i;
}
}
solve(k,l,mid-1,tl,pl);solve(k,mid+1,r,pl,tr);
return;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();m=read();
for(int i=1;i<=n;i++){
t[i]=read();
sum[i]=sum[i-1]+t[i];
s1[i]=s1[i-1]+sum[i]/t[i];
s2[i]=s2[i-1]+1.0/t[i];
}
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++) dp[i][j]=2e18;
}
dp[0][0]=0;
for(int k=1;k<=m;k++) solve(k,1,n,0,n-1);
printf("%lf\n",dp[m][n]);
return 0;
}
/*
*/
CF643D Bearish Fanpages
Description \text{Description} Description
给定一个
n
n
n 个点的基环内向树森林,即给定每个点
i
i
i 的后继
f
i
f_i
fi。
(特别地,保证在任意时刻,这个基环内向森林中的环长都
≥
3
\boldsymbol{\ge 3}
≥3)
在这个限制下,考虑与某个点
i
i
i 距离不超过
1
1
1 的点,也就是
i
i
i 本身,
i
i
i 的后继
f
i
f_i
fi 以及若干个后继为
i
i
i 的点。
不妨假设有
k
k
k 个点的后继为
i
i
i,则我们需要考虑这
k
+
2
k + 2
k+2 个点(这是因为环长
≥
3
\ge 3
≥3,不会出现重复的点)。
称这
k
+
2
k + 2
k+2 个点分别为
i
,
j
0
,
j
1
,
j
2
,
…
,
j
k
i, j_0, j_1, j_2, \ldots , j_k
i,j0,j1,j2,…,jk(
j
0
j_0
j0 为
i
i
i 的后继,
j
1
…
k
j_{1 \ldots k}
j1…k 为后继为
i
i
i 的那
k
k
k 个点)。
现在假设神秘事件发生了,每个点都会进入一些人,第
i
i
i 个点会进入
t
i
t_i
ti 个人。
恰好
⌊
t
i
k
+
2
⌋
\left\lfloor \frac{t_i}{k+2} \right\rfloor
⌊k+2ti⌋ 个人会进入
j
0
,
j
1
,
j
2
,
…
,
j
k
j_0, j_1, j_2, \ldots , j_k
j0,j1,j2,…,jk 号点(每个点都进入
⌊
t
i
k
+
2
⌋
\left\lfloor \frac{t_i}{k+2} \right\rfloor
⌊k+2ti⌋ 个人),剩下的
t
i
−
(
k
+
1
)
⋅
⌊
t
i
k
+
2
⌋
t_i - (k + 1) \cdot \!\left\lfloor \frac{t_i}{k+2} \right\rfloor
ti−(k+1)⋅⌊k+2ti⌋ 个人会留在点
i
i
i。
你需要依次处理 q q q 个操作:
1 i j
:将
f
i
f_i
fi 改为
j
j
j,即把
i
i
i 的后继变成
j
j
j。保证仍然满足上文中的条件。
2 i
:计算当神秘事件发生时,第
i
i
i 个点最终会留下多少人。
3
:计算当神秘事件发生时,每个点内最终留下的人数的最小值和最大值。
注意:每次神秘事件发生都是互不影响的。
Solution \text{Solution} Solution
阴间大模拟题。
容易想到,问题可以转化成对亿些集合的集体修改和亿些单点的修改。
全局
min
,
max
\min,\max
min,max 开两个 set
维护即可。
集体修改考虑懒标记,我用的是线段树维护,对于每个点开一棵以它为父亲的线段树,动态开点插入删除修改瞎做即可。
Code \text{Code} Code
有的地方为了可读性忽略了代码效率,自带
24
24
24 倍常数,成功斩获最劣解
极其屎山,不过看几篇题解似乎都差不多…
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e5+100;
#define ll long long
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m,tim;
ll t[N],w[N],v[N];
int du[N],fa[N];
multiset<ll>mn,mx;
#define mid ((l+r)>>1)
struct tree{
int ls,rs;
ll mn,mx,laz;
}tr[N<<6];
int rt[N],tot;
inline int New(){
++tim;
++tot;tr[tot].mn=2e18;tr[tot].mx=-2e18;
return tot;
}
inline void pushup(int k){
++tim;
tr[k].mn=min(tr[tr[k].ls].mn,tr[tr[k].rs].mn);
tr[k].mx=max(tr[tr[k].ls].mx,tr[tr[k].rs].mx);
return;
}
inline void tag(int k,ll v){
++tim;
if(!k) return;
tr[k].laz+=v;
tr[k].mn+=v;tr[k].mx+=v;return;
}
inline void pushdown(int k){
++tim;
ll o=tr[k].laz;tr[k].laz=0;
if(!o) return;
tag(tr[k].ls,o);tag(tr[k].rs,o);
return;
}
void add(int &k,int l,int r,int p,ll v){
++tim;
if(!k) k=New();
if(l==r){
tr[k].mn=tr[k].mx=v;return;
}
pushdown(k);
if(p<=mid) add(tr[k].ls,l,mid,p,v);
else add(tr[k].rs,mid+1,r,p,v);
pushup(k);
}
ll ask(int k,int l,int r,int p,int op=0){
++tim;
assert(k);
if(l==r){
ll res=tr[k].mn;
if(op) tr[k].mx=-2e18,tr[k].mn=2e18;
return res;
}
pushdown(k);
ll res(0);
if(p<=mid) res=ask(tr[k].ls,l,mid,p,op);
else res=ask(tr[k].rs,mid+1,r,p,op);
pushup(k);
return res;
}
void change(int k,int l,int r,int p,ll v){
++tim;
assert(k);
if(l==r){
tr[k].mn+=v;tr[k].mx+=v;return;
}
pushdown(k);
if(p<=mid) change(tr[k].ls,l,mid,p,v);
else change(tr[k].rs,mid+1,r,p,v);
pushup(k);
return;
}
void upd(int x,ll w){
++tim;
change(rt[fa[x]],1,n,x,w);return;
}
set<int>s;
ll sef[N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
tr[0].mn=2e18;tr[0].mx=-2e18;
n=read();m=read();
for(int i=1;i<=n;i++) t[i]=read();
for(int i=1;i<=n;i++) du[i]=2;
for(int i=1;i<=n;i++){
fa[i]=read();
du[fa[i]]++;
}
for(int i=1;i<=n;i++) w[i]=t[i]/du[i];
for(int i=1;i<=n;i++) v[fa[i]]+=w[i];
for(int i=1;i<=n;i++){
ll val=v[i]+w[fa[i]]+t[i]-w[i]*(du[i]-1);
sef[i]=t[i]-w[i]*(du[i]-1);
//debug("i=%d val=%lld v=%lld w=%lld du\n",i,val);
add(rt[fa[i]],1,n,i,val);
}
for(int i=1;i<=n;i++){
mn.insert(tr[rt[i]].mn);
mx.insert(tr[rt[i]].mx);
}
for(int i=1;i<=m;i++){
int op=read();
if(op==1){
int x=read(),y=read(),f=fa[x];
fa[x]=y;
//debug("erase: f:(%lld %lld) y:(%lld %lld)\n",
// tr[rt[f]].mn,tr[rt[f]].mx,tr[rt[y]].mn,tr[rt[y]].mx);
s.insert(y);s.insert(f);
s.insert(fa[y]);s.insert(fa[f]);
s.insert(fa[fa[y]]);s.insert(fa[fa[f]]);
for(int now:s){
mn.erase(mn.find(tr[rt[now]].mn));mx.erase(mx.find(tr[rt[now]].mx));
}
ll val=ask(rt[f],1,n,x,1);val-=w[f];
tag(rt[f],-w[f]);upd(fa[f],-w[f]);upd(f,-sef[f]);
du[f]--;w[f]=t[f]/du[f];sef[f]=t[f]-w[f]*(du[f]-1);
tag(rt[f],w[f]);upd(fa[f],w[f]);upd(f,sef[f]);
tag(rt[y],-w[y]);upd(fa[y],-w[y]);upd(y,-sef[y]);
du[y]++;w[y]=t[y]/du[y];sef[y]=t[y]-w[y]*(du[y]-1);
tag(rt[y],w[y]);upd(fa[y],w[y]);upd(y,sef[y]);
upd(f,-w[x]);upd(y,w[x]);
val+=w[y];
add(rt[y],1,n,x,val);
for(int now:s){
//if(n==20000) break;
mn.insert(tr[rt[now]].mn);mx.insert(tr[rt[now]].mx);
}
//debug("insert: f:(%lld %lld) y:(%lld %lld)\n",
// tr[rt[f]].mn,tr[rt[f]].mx,tr[rt[y]].mn,tr[rt[y]].mx);
}
else if(op==2){
int x=read();
//if(n<20000)
printf("%lld\n",ask(rt[fa[x]],1,n,x));
}
else{
//if(n<20000)
printf("%lld %lld\n",(*mn.begin()),(*mx.rbegin()));
}
//if(n>=20000) printf("i=%d tot=%d\n",i,tot);
//if(i%100==0){
//printf("i=%d tot=%d time=%lf calc=%d\n",i,tot,1.0*clock()/CLOCKS_PER_SEC,tim);
//}
//if(1.0*clock()/CLOCKS_PER_SEC>4.9){
//printf("i=%d tot=%d\n",i,tot);break;
//}
s.clear();
}
return 0;
}
/*
*/
CF643E Bear and Destroying Subtrees
Description \text{Description} Description
给你一棵初始只有根为 1 1 1 的树。
共有 q q q 次操作。
1 x
表示加入一个以
x
x
x 为父亲的新点。
2 x
表示求以
x
x
x 为根的子树期望最深深度。
每条边都有 1 2 \dfrac{1}{2} 21 的概率断裂。
1 ≤ q ≤ 5 × 1 0 5 1\leq q\leq 5\times 10^5 1≤q≤5×105。
Solution \text{Solution} Solution
这题很 CF…
注意到,当链很长的时候不被断开的概率是极低的。
具体的,当链长超过
60
60
60,对答案的影响完全可以忽略不计。
(经过实测,其实只考虑到
40
40
40 都是没问题的。
这样本题就好办多了。
但依然没有显然。
由于本来答案取
max
\max
max 的性质,设计
d
p
x
,
i
dp_{x,i}
dpx,i 表示子树内答案小于等于
i
i
i 的概率。
那么就有转移:
d
p
f
a
,
k
+
1
=
∏
1
2
×
(
d
p
s
o
n
,
k
+
1
)
dp_{fa,k+1}=\prod \frac{1}{2}\times (dp_{son,k}+1)
dpfa,k+1=∏21×(dpson,k+1)
每次加新点对 60 60 60 个祖先都只会改一层的 dp,所以直接暴力修改即可。
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=5e5+100;
#define ll long long
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m,tim;
double dp[N][62];
int fa[N],tot=1,o=60;
void remove(int x,int k){
if(!fa[x]||k>=o||!x) return;
remove(fa[x],k+1);
dp[fa[x]][k+1]/=(0.5*(dp[x][k]+1));
return;
}
void upd(int x,int k){
if(!fa[x]||k>=o||!x) return;
dp[fa[x]][k+1]*=0.5*(dp[x][k]+1);
//printf("fa=%d *=%lf\n",fa[x],0.5*(dp[x][k]+1));
upd(fa[x],k+1);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();
for(int i=1;i<=o;i++) dp[1][i]=1;
for(int i=1;i<=n;i++){
int op=read(),x=read();
if(op==1){
fa[++tot]=x;x=tot;
for(int j=1;j<=o;j++) dp[x][j]=1;
remove(fa[x],1);
upd(x,0);
}
else{
double ans=o-1;
for(int i=1;i<o;i++) ans-=dp[x][i];
printf("%.10lf\n",ans);
}
}
return 0;
}
/*
*/
CF643F Bears and Juice
Description \text{Description} Description
有 n n n 只熊和若干桶果汁和恰好一桶酒,每一天每只熊会选择一些桶(可能不选)并各喝一 杯,喝到酒的熊会去睡觉并不再回来,通过这个信息,熊们想知道哪个桶里是酒。
只有 p p p 个睡 觉的位置,当睡觉的熊超过了 p p p 只或者所有熊都在睡觉时熊们就失败了。
令 R i R_i Ri 表示在 i i i 天内桶的数量最多少,使得熊可以成功知道酒的位置。令 X i = ( i × R i ) m o d 2 32 X_i = (i\times R_i) \bmod 2^{32} Xi=(i×Ri)mod232,你需要求出 X 1 ⊕ X 2 ⊕ … ⊕ X q X_1 \oplus X_2 \oplus\ldots \oplus X_q X1⊕X2⊕…⊕Xq。
1 ≤ n ≤ 1 0 9 1\leq n\leq 10^9 1≤n≤109, 1 ≤ p ≤ 130 1\leq p\leq 130 1≤p≤130, 1 ≤ q ≤ 2 × 1 0 6 1\leq q \leq 2\times 10^6 1≤q≤2×106。
Solution \text{Solution} Solution
魔法操作。
考虑答案的上界,就是所有熊的反应的不同方案数
对于
i
i
i 天,这个数量就是:
f
i
=
∑
j
=
0
min
(
n
−
1
,
p
)
C
n
j
×
i
j
f_i=\sum_{j=0}^{\min(n-1,p)} C_n^j\times i^j
fi=j=0∑min(n−1,p)Cnj×ij
j
j
j 枚举喝醉的熊的数量,
C
n
j
C_n^j
Cnj 是从
n
n
n 头里选
j
j
j 头,每头可以从
i
i
i 天中选一天喝醉。
然后可以发现,这个上界是可以构造出方案卡到的。
所以答案就是这个。
实现上,由于
n
n
n 过大,不便求
C
n
j
C_n^j
Cnj,可以暴力约分来做。
Code \text{Code} Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=5e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,p,q;
ui a[150],b[150];
ui c[150];
ui gcd(ui a,ui b){return b?gcd(b,a%b):a;}
void init(){
for(int i=0;i<=min(p,n-1);i++){
for(int j=1;j<=i;j++) a[j]=j,b[j]=n-j+1;
for(int j=1;j<=i;j++){
for(int k=1;k<=i;k++){
int g=gcd(a[j],b[k]);
a[j]/=g;b[k]/=g;
}
assert(a[j]==1);
}
c[i]=1;
for(int j=1;j<=i;j++) c[i]*=b[j];
}
return;
}
inline ui ksm(ui x,ui k){
ui res(1);
while(k){
if(k&1) res*=x;
x=x*x;k>>=1;
}
return res;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();p=read();q=read();
init();
ui ans(0);
for(int i=1;i<=q;i++){
ui base(1),res(0);
for(int j=0;j<=min(n-1,p);j++){
res+=c[j]*base;base*=i;
}
ans^=(i*res);
}
printf("%u\n",ans);
return 0;
}
/*
*/
CF643G Choosing Ads
Description \text{Description} Description
给定一个长度为 n n n 的序列和一个整数 p p p。
- 有 m m m 个操作,操作要么是区间赋值,要么是询问区间内出现次数至少占 p % p\% p% 的数。
- 输出询问的答案时,可以包含错的数,也可以重复输出,但对的数一定要在答案中,且输出的数的个数不超过 ⌊ 100 p ⌋ \lfloor \dfrac{100}{p} \rfloor ⌊p100⌋。
- n , m ≤ 1.5 × 1 0 5 n,m \le 1.5 \times 10^5 n,m≤1.5×105, 20 ≤ p ≤ 100 20 \le p \le 100 20≤p≤100。
Solution \text{Solution} Solution
魔法操作。
考虑
p
>
50
p>50
p>50 (严格众数)时如何做。
每次删去两个不同的数,直到删到剩下的种类不超过一种时,如果有严格众数,显然会剩到最后。
类似的,推广到
p
≥
20
p\ge 20
p≥20 的情况。令
k
=
⌊
100
p
⌋
k=\lfloor \dfrac{100}{p}\rfloor
k=⌊p100⌋,每次删去
k
+
1
k+1
k+1 个不同的数,直到不能删为止,也易证如果有符合条件的数最后一定会剩下。
现在要对于区间询问快速模拟这个过程,使用线段树即可,每次暴力
O
(
k
2
)
O(k^2)
O(k2) 暴力合并区间,具体实现建议观看代码。
Code \text{Code} Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,k,m;
struct node{
int val[6],num[6],tot;
}tr[N<<2];
node operator + (const node &a,const node &b){
node res=a;
for(int i=1;i<=b.tot;i++){
bool jd(0);
for(int j=1;j<=res.tot;j++){
if(res.val[j]==b.val[i]){
jd=1;res.num[j]+=b.num[i];break;
}
}
if(jd) continue;
if(res.tot<k){
++res.tot;res.val[res.tot]=b.val[i];res.num[res.tot]=b.num[i];
continue;
}
int pl(1);
for(int j=2;j<=res.tot;j++){
if(res.num[j]<res.num[pl]) pl=j;
}
if(res.num[pl]>b.num[i]){
for(int j=1;j<=res.tot;j++) res.num[j]-=b.num[i];
}
else{
int o=res.num[pl];
res.num[pl]=b.num[i];res.val[pl]=b.val[i];
for(int j=1;j<=res.tot;j++) res.num[j]-=o;
}
}
return res;
}
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
int laz[N<<2];
inline void pushup(int k){tr[k]=tr[ls]+tr[rs];}
inline void tag(int k,int l,int r,int v){
laz[k]=v;tr[k].tot=1;tr[k].num[1]=r-l+1;tr[k].val[1]=v;
return;
}
inline void pushdown(int k,int l,int r){
int o=laz[k];laz[k]=0;
if(!o) return;
tag(ls,l,mid,o);tag(rs,mid+1,r,o);
return;
}
inline void change(int k,int l,int r,int x,int y,int v){
if(x<=l&&r<=y){
tag(k,l,r,v);return;
}
pushdown(k,l,r);
if(x<=mid) change(ls,l,mid,x,y,v);
if(y>mid) change(rs,mid+1,r,x,y,v);
pushup(k);
}
node ask(int k,int l,int r,int x,int y){
if(x<=l&&r<=y) return tr[k];
pushdown(k,l,r);
if(y<=mid) return ask(ls,l,mid,x,y);
else if(x>mid) return ask(rs,mid+1,r,x,y);
else{
node a=ask(ls,l,mid,x,y),b=ask(rs,mid+1,r,x,y);
return a+b;
}
}
int a[N];
void build(int k,int l,int r){
if(l==r){
tr[k].tot=1;tr[k].num[1]=1;tr[k].val[1]=a[l];
return;
}
build(ls,l,mid);build(rs,mid+1,r);
pushup(k);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();m=read();k=100/read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
int op=read(),l=read(),r=read();
if(op==1){
int x=read();
change(1,1,n,l,r,x);
}
else{
node res=ask(1,1,n,l,r);
printf("%d ",res.tot);
for(int j=1;j<=res.tot;j++) printf("%d ",res.val[j]);
putchar('\n');
}
}
return 0;
}
/*
*/