CodeForces:643(VK cup)

前言

毒瘤比赛,三紫二黑(而且感觉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 1n。同时又有 n n n 种颜色,从编号依次为 1 … n 1 \dots n 1n。第 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 n5000

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 4n1000

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 acx...ydb,再加两条 a → x a\to x ax y → b y\to b yb 的边满足要求,总边数是 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 ljrj。任何一个关卡在且仅在一个级别中。

然后,一名玩家将会从第 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=ljitxti 的概率在 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+(1p)×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=lrtisumisuml1
也就是:
∑ 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=lrtisumisuml1×i=lrti1
∑ 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} suml1 的值应该相应的变大(或者至少不应该变小),所以转移点有单调性的。
直接上分治即可,时间复杂度 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} j1k 为后继为 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 1q5×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 X1X2Xq

1 ≤ n ≤ 1 0 9 1\leq n\leq 10^9 1n109 1 ≤ p ≤ 130 1\leq p\leq 130 1p130 1 ≤ q ≤ 2 × 1 0 6 1\leq q \leq 2\times 10^6 1q2×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=0min(n1,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,m1.5×105 20 ≤ p ≤ 100 20 \le p \le 100 20p100

Solution \text{Solution} Solution

魔法操作。
考虑 p > 50 p>50 p>50 (严格众数)时如何做。
每次删去两个不同的数,直到删到剩下的种类不超过一种时,如果有严格众数,显然会剩到最后。

类似的,推广到 p ≥ 20 p\ge 20 p20 的情况。令 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;
}
/*
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值