\(5.4\ NOI\)模拟

\(T1\)

想到分讨,但是暴力输出一下方案之后有很多特别的情况要讨论,就弃了...

假设\(a\)是原序列,\(b\)是我们得到的序列

设\(i\)是最长公共前缀,\(j\)是最长公共后缀

我们假设询问的是整个序列,若\(i+j=n-1\)那我们的方案数是\(m-1\),较为显然

否则\(i+j<=n-2\)

首先比较显然的是,由于已知串确定,可以根据最长公共前后缀长度进行分类,即可不重不漏的计算每一种情况

那么\(a[i+1]\)和\(a[n-j]\)至少有一个出现在最长公共序列(可能取出来的公共序列不是同一个)里

我们枚举\(i,j\)

假设\(i+j<=n-2\)

\(Sit_1:a[i+1]\neq a[i+2]\)可以增加\(m-1\)种情况

\(Sit_2:a[n-j]\neq a[n-j-1]\)可以增加\(m-1\)种情况

\(Sit_3:a[i+1]\neq a[i+2]\)且\(a[n-j]\neq a[n-j-1]\)那么就\(a[i+1...n-j-2]=a[i+3...n-j],\)可增加\(1\)种情况

我们的答案是\(Sit_1+Sit_2-Sit_3\)

就是说我们可能出现\(1,2\)重复计算情况减去一部分就好了

那么暴力的话可以枚举\(i,j\)进行计算,可以发现,前两种情况,我们可以合并统计

对于第三种情况,我们只需要找到所有满足条件的\(i,j\)即可

那么枚举\(i\)那么\(j>=n-i-2-lcp(a[i+1..n],a[i+3...n])\)既满足\(a[n-j]\neq a[n-j-1],\)用\(Sit_3\)反证即可

合并的意思是,还是说 颜色块数\(\times n\times (m-1)\) 就好了,就没有那么多的分讨了

\(lcp\)求和的话发现有单调性,可使用双指针,复杂度降到\(O(n)\)

\(T2\)

\(Give\ up\)

教练说必须过,就写了一个下午加一个晚上

#include<bits/stdc++.h>
#define MAXN 18900005
#define MAXM 200005
#define ll long long
using namespace std;
const int mod=998244353,INF=1000000000;
int my_pow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
{
res=(1ll*res*a)%mod;
}
a=(1ll*a*a)%mod;
b>>=1;
}
return res;
}
namespace Seg
{
int cnt=1;
queue<int> rub;
struct node
{
int l,r,siz,sum,mul;
}tr[MAXN];
int New()
{
if(rub.size()==0) return cnt++;
else
{
int id=rub.front();
rub.pop();
tr[id].sum=tr[id].siz=tr[id].l=tr[id].r=0;
tr[id].mul=1;
return id;
}
}
void Init(int x)
{
tr[x].l=tr[x].r=tr[x].siz=tr[x].sum=0;
tr[x].mul=1;
}
void Del(int x)
{
if(x==0) return ;
rub.push(x);
Del(tr[x].l);
Del(tr[x].r);
}
void push_up(int x)
{
((tr[x].sum=(tr[tr[x].l].sum+tr[tr[x].r].sum)%mod)+=mod)%=mod;
tr[x].siz=(tr[tr[x].l].siz+tr[tr[x].r].siz);
((tr[x].mul=(1ll*tr[tr[x].l].mul*tr[tr[x].r].mul)%mod)+=mod)%=mod;
}
void change(int &x,int l,int r,ll poz,ll k)
{
x=New();
tr[x].sum=((k*poz%mod)+mod)%mod;
tr[x].mul=my_pow(poz,k);
tr[x].siz=k;
if(l==r) return ;
int mid=(l+r)>>1;
if(poz<=mid) change(tr[x].l,l,mid,poz,k);
else change(tr[x].r,mid+1,r,poz,k);
}
int Merge(int x,int y,int l,int r)
{
if(x==0||y==0) return x+y;
tr[x].sum=(tr[x].sum+tr[y].sum)%mod;
tr[x].mul=(1ll*tr[x].mul*tr[y].mul)%mod;
tr[x].siz=tr[x].siz+tr[y].siz;
if(l==r)
{
rub.push(y);
return x;
}
int mid=(l+r)>>1;
tr[x].l=Merge(tr[x].l,tr[y].l,l,mid);
tr[x].r=Merge(tr[x].r,tr[y].r,mid+1,r);
rub.push(y);
return x;
}
void spilt(int x,int l,int r,int k,int &rt1,int &rt2)
{
if(x==0)
{
rt1=rt2=0;
return ;
}
if(l==r)
{
if(k==0) rt1=0,rt2=x;
else if(k==tr[x].siz) rt1=x,rt2=0;
else
{
rt1=x;
rt2=New();
tr[rt2].siz=tr[x].siz-k;
tr[rt2].sum=(1ll*l*tr[rt2].siz%mod+mod)%mod;
tr[rt2].mul=my_pow(l,tr[rt2].siz);
tr[rt1].siz=k;
tr[rt1].sum=(1ll*l*tr[rt1].siz%mod+mod)%mod;
tr[rt1].mul=my_pow(l,tr[rt1].siz);
}
return ;
}
int mid=(l+r)>>1;
if(tr[tr[x].l].siz<=k)
{
rt1=x;
rt2=New();
spilt(tr[x].r,mid+1,r,k-tr[tr[x].l].siz,tr[rt1].r,tr[rt2].r);
}
else
{
rt1=New();
rt2=x;
spilt(tr[x].l,l,mid,k,tr[rt1].l,tr[rt2].l);
}
push_up(rt1); push_up(rt2);
}
int Get_Max(int x,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(tr[tr[x].r].siz) return Get_Max(tr[x].r,mid+1,r);
return Get_Max(tr[x].l,l,mid);
}
int Get_Min(int x,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(tr[tr[x].l].siz) return Get_Min(tr[x].l,l,mid);
return Get_Min(tr[x].r,mid+1,r);
}
}
namespace FHQ
{
int cnt=1;
int rt=0;
vector<int>rub;
struct node
{
int l,r,siz,key,rt[2];;
bool tag,rtag,ntag;
}tr[MAXM];
int New()
{
if(rub.size()==0) return cnt++;
else
{
int id=rub.back();
rub.pop_back();
tr[id].key=rand();
tr[id].l=tr[id].r=tr[id].siz=tr[id].rt[0]=tr[id].rt[1]=0;
tr[id].tag=tr[id].rtag=tr[id].ntag=0;
return id;
}
}
int New(int x,int k)
{
int id;
if(rub.size()==0) id=cnt++;
else id=rub.back(),rub.pop_back();
tr[id].key=rand();
tr[id].l=tr[id].r=0;
tr[id].tag=tr[id].rtag=tr[id].ntag=0;
tr[id].siz=k;
Seg::change(tr[id].rt[0],-INF,INF,x,k);
Seg::change(tr[id].rt[1],-INF,INF,-x,k);
return id;
}
void Del(int x)
{
if(x==0) return;
Seg::Del(tr[x].rt[0]);
Seg::Del(tr[x].rt[1]);
rub.push_back(x);
Del(tr[x].l);
Del(tr[x].r);
}
void push_up(int x)
{
tr[x].siz=(tr[tr[x].l].siz+tr[tr[x].r].siz+Seg::tr[tr[x].rt[0]].siz);
}
void Rev(int x)
{
if(x==0) return ;
tr[x].rtag^=1;
tr[x].tag^=1;
swap(tr[x].l,tr[x].r);
}
void Neg(int x)
{
if(x==0) return ;
tr[x].ntag^=1;
tr[x].tag^=1;
swap(tr[x].rt[0],tr[x].rt[1]);
}
void pd(int x)
{
if(tr[x].rtag)
{
Rev(tr[x].l);
Rev(tr[x].r);
tr[x].rtag=0;
}
if(tr[x].ntag)
{
Neg(tr[x].l);
Neg(tr[x].r);
tr[x].ntag=0;
}
}
void spilt(int rt,int k,int &rt1,int &rt2)
{
if(rt==0)
{
rt1=0; rt2=0;
return ;
}
pd(rt);
if(tr[tr[rt].l].siz>=k)
{
rt2=rt;
spilt(tr[rt].l,k,rt1,tr[rt2].l);
push_up(rt2);
}
else if(tr[tr[rt].l].siz+Seg::tr[tr[rt].rt[0]].siz<=k)
{
rt1=rt;
spilt(tr[rt].r,k-(tr[tr[rt].l].siz+Seg::tr[tr[rt].rt[0]].siz),tr[rt1].r,rt2);
push_up(rt1);
}
else
{
rt1=rt;
rt2=New();
tr[rt2].r=tr[rt1].r;
tr[rt1].r=0;
k-=tr[tr[rt].l].siz;
int len=Seg::tr[tr[rt].rt[0]].siz;
if(tr[rt].tag) swap(tr[rt].rt[0],tr[rt].rt[1]);
//这里交换的目的是因为大小反过来了,反正是排完序了,我们两个分裂方式不想变
//那就交换一下
//最后再交换回来得到,不得不说,这个真的很妙
Seg::spilt(tr[rt].rt[0],-INF,INF,k,tr[rt1].rt[0],tr[rt2].rt[0]);
Seg::spilt(tr[rt].rt[1],-INF,INF,len-k,tr[rt2].rt[1],tr[rt1].rt[1]);
if(tr[rt].tag)
{
swap(tr[rt1].rt[0],tr[rt1].rt[1]);
swap(tr[rt2].rt[0],tr[rt2].rt[1]);
tr[rt2].tag=1;
}
push_up(rt1);
push_up(rt2);
}
}
int Merge(int rt1,int rt2)
{
if(rt1==0||rt2==0) return rt1+rt2;
pd(rt1); pd(rt2);
//反正平衡树维护的是区间,合并一下表示排序之后也是无影响的
if(tr[rt1].key>=tr[rt2].key)
{
tr[rt1].r=Merge(tr[rt1].r,rt2);
push_up(rt1);
return rt1;
}
else
{
tr[rt2].l=Merge(rt1,tr[rt2].l);
push_up(rt2);
return rt2;
}
}
//感觉很像一个珂朵莉啊
int Cover(int rt,int l,int r,int k)
{
int rt1,rt2,rt3;
spilt(rt,r,rt1,rt3);
spilt(rt1,l-1,rt1,rt2);
Del(rt2);
rt2=New(k,r-l+1);
return Merge(Merge(rt1,rt2),rt3);
}
int Reverse(int rt,int l,int r)
{
int rt1,rt2,rt3;
spilt(rt,r,rt1,rt3);
spilt(rt1,l-1,rt1,rt2);
Rev(rt2);
return Merge(Merge(rt1,rt2),rt3);
}
int Negat(int rt,int l,int r)
{
int rt1,rt2,rt3;
spilt(rt,r,rt1,rt3);
spilt(rt1,l-1,rt1,rt2);
Neg(rt2);
return Merge(Merge(rt1,rt2),rt3);
}
int Compress(int x)
{
if(x==0) return 0;
pd(x);
tr[x].l=Compress(tr[x].l);
tr[x].r=Compress(tr[x].r);
tr[x].rt[0]=Seg::Merge(tr[x].rt[0],Seg::Merge(tr[tr[x].l].rt[0],tr[tr[x].r].rt[0],-INF,INF),-INF,INF);
tr[x].rt[1]=Seg::Merge(tr[x].rt[1],Seg::Merge(tr[tr[x].l].rt[1],tr[tr[x].r].rt[1],-INF,INF),-INF,INF);
if(tr[x].l) rub.push_back(tr[x].l);
if(tr[x].r) rub.push_back(tr[x].r);
return x;
}
int Sort(int rt,int l,int r)
{
int rt1,rt2,rt3;
spilt(rt,r,rt1,rt3);
spilt(rt1,l-1,rt1,rt2);
rt2=Compress(rt2);
tr[rt2].l=tr[rt2].r=0;
tr[rt2].tag=0;
ll Sum=Seg::tr[tr[rt2].rt[0]].sum;
ll MAX=Seg::Get_Max(tr[rt2].rt[0],-INF,INF);
ll MIN=Seg::Get_Min(tr[rt2].rt[0],-INF,INF);
ll Mul=Seg::tr[tr[rt2].rt[0]].mul;
printf("%lld %lld %lld %lld \n",(Sum%mod+mod)%mod,(MAX%mod+mod)%mod,(MIN%mod+mod)%mod,(Mul%mod+mod)%mod);
return Merge(rt1,Merge(rt2,rt3));
}
}
int n,q,a[MAXN];
signed main()
{
scanf("%d%d",&n,&q);
using namespace FHQ;
Seg::Init(0);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
rt=Merge(rt,New(a[i],1));
}
for(int i=1,opt,l,r,k;i<=q;i++)
{
scanf("%d%d%d",&opt,&l,&r);
if(opt==1)
{
scanf("%d",&k);
rt=Cover(rt,l,r,k);
}
if(opt==2)
{
rt=Negat(rt,l,r);
}
if(opt==3)
{
rt=Reverse(rt,l,r);
}
if(opt==4)
{
rt=Sort(rt,l,r);
}
}
}

\(T3\)

一开始想的是枚举最大值计算有多少种情况,发现在有重复数字时候寄了

错误式子是

\(\large \frac{\sum_{max}max\times C(Num_{min},k-1)}{C(Num,k)}\)

很容易发现一件事,就是我们枚举了最大值可是并不是知道最大值是谁,因为这个是有标号的

那么我们假定同样大小编号大的比较大的话,就能保证不重复了

设\(a\)为降序排序序列,设\(b\)为升序排序序列

我们考虑答案计算

\(\sum_{i=l}^r C(r-i,k-1)\times a[i]\)

这样复杂度不是很优

考场上看到了\(\sum k<=100000,\)感觉\(k\)的种类不是很多,就想着全部预处理出来,没去想根号分治...

考虑进行根号分治

我们要计算区间\((l,r)\)

此时序列降序

我们提前预处理\(dp[i][j]\)表示\(k=i\)时,\(\sum_{z=1}^j C(j-z,k-1)\times b[z],\)也就是\(1-j\)区间选\(k\)个数字最大值的总和

我们现在要求\(\sum_{z=l}^r C(r-z,k-1)\times b[z]=dp[k][r]-\sum_{z=1}^{l-1}C(r-z,k-1)\times b[z]\)

考虑把他转化到\(dp[k][l-1]\)上

\(\sum_{z=1}^{l-1}C(r-z,k-1)\times b[z]=\sum_{j}dp[j][l-1]\times C(r-l+1,k-j)\)

组合意义比较显然,假设我们的贡献在前面,我们可以枚举前面选了多少个,然后后面任选的减去就好了,然后就\(hhh\)了

\(dp[i][j]=dp[i-1][j-1]+dp[i][j-1]\)

把式子拆开

\(\large \sum_{z=1}^j C(j-z,i-1)b[z]=\sum_{z=1}^{j-1} C((j-1)-z,i-2)b[z]+\sum_{z=1}^{j-1} C((j-1)-z,i-1)b[z]\)

较为显然,直接是组合数递推式的样子

#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define MAXN 100005
#define Lim 300
using namespace std;
int inv[MAXN],fac[MAXN],b[MAXN],a[MAXN],n,q;
int dp[Lim+5][MAXN];
inline int rd(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
void Init()
{
inv[0]=fac[0]=1;
inv[1]=fac[1]=1;
for(int i=2;i<MAXN;i++)
{
fac[i]=(fac[i-1]*i)%mod;
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(int i=1;i<MAXN;i++)
{
inv[i]=(inv[i-1]*inv[i])%mod;
}
}
int C(int n,int m)
{
if(m<0||m>n) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int my_pow(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()
{
scanf("%lld%lld",&n,&q);
Init();
for(int i=1;i<=n;i++)
{
a[i]=rd();
b[i]=a[i];
}
sort(a+1,a+1+n);
sort(b+1,b+1+n);
reverse(a+1,a+1+n);
for(int i=1;i<=n;i++) dp[1][i]=(dp[1][i-1]+a[i])%mod;
for(int i=2;i<=Lim;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=(dp[i-1][j-1]+dp[i][j-1])%mod;
}
}
// for(int i=1;i<=n;i++)
// {
// cout<<b[i]<<" ";
// }
// cout<<"\n";
for(int i=1,l,r,k;i<=q;i++)
{
// cout<<"shu: "<<l<<" "<<r<<"\n";
l=rd();
r=rd();
k=rd();
l=lower_bound(b+1,b+1+n,l)-b;
r=upper_bound(b+1,b+1+n,r)-b-1;
// cout<<"Sit: "<<l<<" "<<r<<"\n";
printf("%lld ",r-l+1);
swap(l,r);
l=n-l+1;
r=n-r+1;
int Ans=0;
if(k==0)
{
puts("0");
continue;
}
if(k>r-l+1)
{
puts("-1");
continue;
}
if(k>Lim)
{
for(int j=l;j<=r;j++)
{
Ans=(Ans+C(r-j,k-1)*a[j])%mod;
}
}
else
{
Ans=dp[k][r]%mod;
for(int j=1;j<=k;j++)
{
Ans-=C(r-l+1,k-j)*dp[j][l-1]%mod;
}
Ans%=mod;
Ans+=mod;
Ans%=mod;
}
Ans=(Ans*my_pow(C(r-l+1,k),mod-2)%mod);
printf("%lld\n",Ans);
// cout<<Ans<<"\n";
}
return 0;
}
/*
7 3
83 74 100 89 95 79 72
90 100 3
80 89 1
70 79 2
*/

5.4 NOI模拟的更多相关文章

  1. 5.30 NOI 模拟

    $5.30\ NOI $模拟 高三大哥最后一次模拟考了,祝他们好运 \(T1\)装箱游戏 显然可以将四种字母之间的空缺当做状态枚举 那么这道题就很显然了 #include<bits/stdc++ ...

  2. 5.23 NOI 模拟

    $5.23\ NOI $模拟 \(T1\)简单的计算几何题 \(zjr:\)我当时没改,那么自己看题解吧 倒是有个简单的随机化方法(能获得\(72pts,\)正确性未知)\(:\) 随机两条切椭圆的平 ...

  3. 5.6 NOI模拟

    \(5.6\ NOI\)模拟 明天就母亲节了,给家里打了个电话(\(lj\ hsez\)断我电话的电,在宿舍打不了,只能用教练手机打了) 其实我不是很能看到自己的\(future,\)甚至看不到高三的 ...

  4. NOI模拟赛 Day1

    [考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...

  5. NOI 模拟赛 #2

    得分非常惨惨,半个小时写的纯暴力 70 分竟然拿了 rank 1... 如果 OYJason 和 wxjor 在可能会被爆踩吧 嘤 T1 欧拉子图 给一个无向图,如果一个边集的导出子图是一个欧拉回路, ...

  6. 【2018.12.10】NOI模拟赛3

    题目 WZJ题解 大概就是全场就我写不过 $FFT$ 系列吧……自闭 T1 奶一口,下次再写不出这种 $NTT$ 裸题题目我就艹了自己 -_-||| 而且这跟我口胡的自创模拟题 $set1$ 的 $T ...

  7. 6.28 NOI模拟赛 好题 状压dp 随机化

    算是一道比较新颖的题目 尽管好像是两年前的省选模拟赛题目.. 对于20%的分数 可以进行爆搜,对于另外20%的数据 因为k很小所以考虑上状压dp. 观察最后答案是一个连通块 从而可以发现这个连通块必然 ...

  8. NOI模拟赛Day5

    T1 有and,xor,or三种操作,每个人手中一个数,求和左边进行某一种运算的最大值,当t==2时,还需要求最大值的个数. test1 20% n<=1000 O(n^2)暴力 test2 2 ...

  9. NOI模拟赛Day4

    看到成绩的时候我的内心** woc第一题写错了呵呵呵呵呵呵呵呵 人不能太浪,会遭报应的** ------------------------------------------------------ ...

随机推荐

  1. 1.还不会部署高可用的kubernetes集群?看我手把手教你使用二进制部署v1.23.6的K8S集群实践(上)

    公众号关注「WeiyiGeek」 设为「特别关注」,每天带你玩转网络安全运维.应用开发.物联网IOT学习! 本章目录: 0x00 前言简述 0x01 环境准备 主机规划 软件版本 网络规划 0x02 ...

  2. awk内建函数

    内建函数 length() 获得字符串长度 cat score.txt Marry 2143 78 84 77 Jack 2321 66 78 45 Tom 2122 48 77 71 Mike 25 ...

  3. 学习Java的第十七天——大数字运算

    学习内容:大数字运算 代码实现: package 数字处理类; import java.math.BigInteger; public class BigIntegerDemo { public st ...

  4. ftp多文件压缩下载

    @GetMapping(value = "/find") public String findfile(String filePath, String fileNames, Htt ...

  5. Java基本运算

    目录 运算符 运算符优先级 运算 自增(++)自减(--)运算 数学运算(Math类) 逻辑运算 位运算 拓展运算符 三元运算符 视频课程 运算符 Java语言支持如下运算符: 算术运算符: +, - ...

  6. PowerShell 定时刷新查看文件内容

    get-content .\1.txt -ReadCount 0 -Tail 5 -Wait

  7. 微信access_token缓存与更新

    由于Access Token有效期只有7200秒,而每天调用获取的次数只有2000次,所以需要将Access Token进行缓存来保证不触发超过最大调用次数.另外在微信公众平台中,绝大多数高级接口都需 ...

  8. Vue MD5加密你用吗?

    安装 npm install --save js-md5 1.按需引入(在你需要的项目中引入) 引入: import md5 from 'js-md5' 使用: md5('加密信息') 2.全局引入( ...

  9. Maven的安装 和idea的配置

    Maven的安装 和idea的配置 工欲善其事 必先利其器 1 下载maven 官网 下滑 找到Files tar.gz 是linux系统的 .zip window系统 2 maven安装和配置到环境 ...

  10. sql server 跨IP库更新表字段(OPENDATASOURCE 、update)

    --跨ip库更新表字段 update uat set goodsType=dev.goodsType from OPENDATASOURCE('SQLOLEDB','Data Source=127.0 ...