上 分 大 胜 利
感觉不久就能上2100了(兴奋地搓手手)
化学作业好多
EDU场没打 睡觉了 今天补了题
set答案是最大值减最小值-相邻两个最大的差,维护原数组和差分数组,每次更改找到前驱后继的两个区间改一下就行了。
奥妙计数
首先给 d d d数组排序,对于一个 b [ i ] b[i] b[i] 假设有 k k k个数大于等于他,那么在第 k k k个比他大的数后面才会有伤害。对于比 b [ i ] b[i] b[i]大的数,他有 1 − a [ i ] k 1-\frac{a[i]}{k} 1−ka[i]的概率造成伤害,对于比他小的数,考虑 k k k个数之间有 k + 1 k+1 k+1个空,在第 a + 1 a+1 a+1个空以后的才会造成伤害,也就是 1 − a [ i ] + 1 k + 1 1-\frac{a[i]+1}{k+1} 1−k+1a[i]+1的概率,再乘上和就是期望。
其实也可以大力组合数学算出总方案数,除以 n ! n! n!,可惜式子推得我有点晕。
#include
#define int long long
#define N 300015
#define mod 998244353
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
#define VI vector
using namespace std;
int n,m,d[N],a[N],b[N],pre[N],suf[N];
int qpow(int a,int b){
int res = 1;
while(b){
if(b&1) res = (res*a)%mod;
a = a*a%mod;
b >>= 1;
}
return res;
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%lld%lld",&n,&m);
rep(i,1,n) scanf("%lld",&d[i]);
rep(i,1,m) scanf("%lld%lld",&a[i],&b[i]);
sort(d+1,d+n+1);
rep(i,1,n) pre[i] = pre[i-1]+d[i]%mod;
per(i,1,n) suf[i] = suf[i+1]+d[i]%mod;
rep(i,1,m){
int pos = lower_bound(d+1,d+n+1,b[i])-d;
//cout << pos << endl;
int k = n-pos+1;
if(a[i] > k || a[i] == n){
//puts("fuck");
printf("0\n");
}else{
int pr = ((pre[pos-1]*(k-a[i]+1)%mod)%mod*qpow(k+1,mod-2)%mod)%mod,su = ((suf[pos]*(k-a[i])%mod)%mod*qpow(k,mod-2)%mod)%mod;
//cout <
printf("%lld\n", (pr+su)%mod);
}
}
return 0;
}
线段树 / 神必哈希
首先讲线段树做法,我们设 o [ i ] [ j ] o[i][j] o[i][j]表示值为 i i i的数第 j j j次出现的位置, p o s [ a [ i ] ] pos[a[i]] pos[a[i]]表示当前是第几次出现,显然我们每次合法区间分为两种
我们要找到对 n n n个数都合法的区间,用线段树维护区间最大值 m x [ i ] mx[i] mx[i]和最大值的个数 c n t [ i ] cnt[i] cnt[i],如果 m x [ 1 ] = = n mx[1] == n mx[1]==n,答案加上 c n t [ 1 ] cnt[1] cnt[1],每次往后移的时候把前一位赋值成 − i n f -inf −inf。
看 z y y zyy zyy的代码看了二十分钟才看明白,而 z y y zyy zyy只花了八分钟就做出了这道题。
#include
#define ll long long
#define N 500015
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
#define VI vector
#define ls (p<<1)
#define rs (p<<1|1)
using namespace std;
int n,a[N],pos[N];
VI o[N];
namespace seg{
int lazy[N<<2],mx[N<<2],cnt[N<<2];
void build(int p,int l,int r){
mx[p] = 0;cnt[p] = r-l+1;
if(l==r) return;
int mid = (l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void change(int p,int l,int r,int x,int y,int v){
if(x <= l&&r <= y){
lazy[p] += v;
mx[p] += v;
return;
}
int mid = (l+r)>>1;
if(x <= mid) change(ls,l,mid,x,y,v);
if(y > mid) change(rs,mid+1,r,x,y,v);
mx[p] = max(mx[ls],mx[rs]);cnt[p] = 0;
if(mx[p] == mx[ls]) cnt[p] += cnt[ls];
if(mx[p] == mx[rs]) cnt[p] += cnt[rs];
mx[p] += lazy[p];
}
}
using namespace seg;
void change(int x,int v){
change(1,0,n,o[x][pos[x]],o[x][pos[x]+1]-1,v);
if(pos[x]+3 < o[x].size()-1)
change(1,0,n,o[x][pos[x]+3],o[x][pos[x]+4]-1,v);
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%d",&n);
rep(i,1,n) scanf("%d",&a[i]);
rep(i,1,n) o[i].pb(0);
rep(i,1,n) o[a[i]].pb(i);
rep(i,1,n) o[i].pb(n+1);
build(1,0,n);
rep(i,1,n) change(i,1);
ll ans = 0;
rep(i,1,n){
change(1,0,n,i-1,i-1,-inf);
if(mx[1] == n) ans += cnt[1];
change(a[i],-1);
pos[a[i]]++;
change(a[i],1);
}
printf("%lld\n",ans);
return 0;
}
哈希做法:
我们对每一个前缀求一个数组 p [ i ] = p [ i ] 1 , p [ i ] 2 , . . . , p [ i ] n p[i] = {p[i]_1,p[i]_2,...,p[i]_n} p[i]=p[i]1,p[i]2,...,p[i]n,其中 p [ x ] i p[x]_i p[x]i表示前 x x x个中 i i i出现了几次,对 3 3 3取模。
显然如果 p [ i ] = = p [ j ] p[i] == p[j] p[i]==p[j]那么这个区间显然是合法的,考虑到题目要求恰好三个而非三的倍数个,我们进行双指针,维护 p [ i ] p[i] p[i]。可是如果直接维护数组会出大问题,所以我们对数组求哈希,可以直接滚动哈希,但还有种神必 t r i c k trick trick,我们对每个 i i i求一个 l o n g l o n g long \ long long long范围内的随机数 r [ i ] r[i] r[i],那么 h a s h [ x ] = ∑ i p [ x ] i ∗ r [ i ] hash[x] = \sum_{i}p[x]_i*r[i] hash[x]=∑ip[x]i∗r[i],碰撞概率是 1 2 63 \frac{1}{2^{63}} 2631
关于随机哈希,暑假集训时有道题有同样的 t r i c k trick trick。
哈希
我们可以对每个数求一个随机数 r [ i ] r[i] r[i],维护区间随机数的异或值,如果区间异或值等于广义排列的异或值,则正确。
#include
#define ll long long
#define N 1000015
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
#define VI vector
using namespace std;
ll sum[N];
unsigned ll c[N],f[N],pre[N];
int n,m,q,a[N];
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
srand(time(0));
scanf("%d%d",&n,&m);
rep(i,1,n) scanf("%d",&a[i]);
rep(i,1,m) f[i] = rand()*rand(),pre[i]=pre[i-1]^f[i];
rep(i,1,n) for(int x=i;x<=n;x+=lowbit(x)) sum[x]+=a[i],c[x]^=f[a[i]];
scanf("%d",&q);
while(q--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(u==2){
for(int x=v;x<=n;x+=lowbit(x)) sum[x]+=w-a[v],c[x]^=f[a[v]]^f[w];
a[v]=w;
}else{
ll s=0;unsigned ll msk=0;
int len = w-v+1;
for(int x=w;x;x-=lowbit(x)) s+=sum[x],msk^=c[x];
for(int x=v-1;x;x-=lowbit(x)) s-=sum[x],msk^=c[x];
s<<=1;
int fi=(s/len+1-len)/2,la=fi+len-1;
if(msk==(pre[la]^pre[fi-1])) puts("YES");
else puts("NO");
}
}
return 0;
}
更普遍的做法是维护区间和,区间平方和。
Gugugu
物理课
学了一天文化课,晚上MO考试三道大题只会一道/kk
cf狂掉五十分
上午睡大觉,下午队友带飞ccpc。
最后只有五题做出来了。
对于每个质数,和 2 2 2连边最优,对于合数,和一个因子连边最优。
答案就是质数和*2+合数和。
m i n _ 25 min\_25 min_25筛求出 1 1 1~ n n n的质数和。
s g sg sg函数,把每个数的指数和异或起来就是答案,因子 2 2 2的指数最多算一次。
队友打了一发 p o l l a r d _ r o u pollard\_rou pollard_rou挂了,赛后发现暴力分解就能过/kk