[NOIP2018模拟赛10.23]发呆报告
闲扯
考场看了眼题目感觉很难,一个小时敲完了所有暴力...嗯然后就在那里发呆什么事也没做
T3考场上把数据结构想了个遍都不会完成1操作,现在看这种思路其实之前也接触过...
比较玄学的一件事情就是T1一开始测得有40分结果过了会看爆0了,难不成被续走了(然而后面测了一下真的爆0了)
太菜了不讲了
T1 sequence
首先通过大佬博客了解一下\(k\)阶前缀和: https://blog.csdn.net/hrbust_cx/article/details/82431567
通过看那张图你会发现这个\(k\)阶前缀和也真神奇, 仔细观察\(a[3]\)那行,我们我们规定列编号从\(0\)开始,那么\(a[3][0]= C^3_3 = 1\) \(a[3][1] = C^4_3\) \(a[3][2] =C^5_3\) \(a[3][3]= C^6_3\)
也就是说如果你在第\(p\)阶使\(a[p][0]+1\),那么前缀和后\(a[p+k][i] = C^{k+i}_k\)
再看看我们要维护的式子,于是就可以差分开搞了
但是发现我们从\(p\)到\(p+k\)每一(阶)行在\(r+1\)处都需要差分一下,这个可以发现就是一个组合数,可以根据上面结论,也可以根据它斜着看就是个杨辉三角的性质
我们只要预先处理好\(C^i_j (1<=i<=n+k, 0<=j<=k)\)就可以\(O((n+m)k)\)解决这道题
话说又是CF题: https://www.luogu.org/problemnew/show/CF407C
/*
code by RyeCatcher
*/
const int maxn=500035;
const int maxc=22;
const int inf=0x7fffffff;
const ll INF=1e17+19260817;
const ll P=1e9+7;
int n,m;
ll f[maxn][maxc],c[maxn][maxc],s[maxn][maxc];
int a[maxn];
int main(){
int x,y,l,r,k;
//FO(sequence);
read(n),read(m);
for(ri i=0;i<=n+22;i++){
c[i][0]=1;
for(ri j=1;j<=20;j++){
c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
}
}
while(m--){
read(l),read(r),read(k);
f[l][k]++;
for(ri i=0;i<=k;i++)f[r+1][k-i]=(P+(f[r+1][k-i]-c[i+r-l][i])%P)%P;//计算右边的差分项
}
for(ri i=20;i>=0;i--){
for(ri j=1;j<=n;j++)
f[j][i]=(f[j][i]+f[j-1][i]+f[j][i+1])%P;//等价于上一阶的前缀和,也是种计算的方法
}
for(ri i=1;i<=n;i++)printf("%lld ",f[i][0]);
return 0;
}
T2 bomb
讲题人: 这是一个经典的DP状态转移
我: ???
有一个部分分,就是求\(n\)个节点的带标号无向联通图个数.设\(f[i]\)为\(i\)个节点的带标号无向连通图个数,\(g[i]\)表示\(i\)个节点能构成带标号图的数量,易知\(g[i]= 2 ^{ C^2 _i } = 2 ^{ i \times (i-1) /2}\)
正难则反(我现在还记得卫中说的这句话),我们用\(i\)个节点能构成图的个数减去不连通的情况
\(f[i] =g[i] - \sum^{i-1}_{j=1} g[i-j] \times C^{i-1}_{j-1} \times f[j]\)
我们相当于枚举有\(j\)个点是连通的,剩下\(i-j\)个不连通与其不连通,这个不连通的图有\(g[i-j]\)中
由于是有标号的,我们钦定1号点在联通的块中,那么剩下构成联通的图的点数标号集合就有\(C^{i-1}_{j-1}\)种可能,联通图的形态也有\(f[j]\)种可能
再类比于之前一道模拟赛题将恰好为\(k\)转化为小于等于\(k\)的减去小于等于\(k-1\)的.在这题我们发现这样转化会更好处理
用\(ff[i]\)表示\(i\)个节点,且联通块大小小于等于k的图的个数
\(ff[i] = \sum^{min(i,k)}_{j=1} C^{i-1}_{j-1} \times f[j] \times ff[i-j]\)
我们类似的分成两个部分乘法原理就好了
/*
code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::min;
using std::max;
using std::priority_queue;
using std::queue;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
static char buf[SIZE],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=2005;
const ll inf=1e17+19260817;
const ll P=998244353;
ll f[maxn],ff[maxn],fff[maxn],g[maxn],c[maxn][maxn];
int n,k;
ll ksm(ll p,int c){
ll ans=1;
while(c){
if(c&1)ans=ans*p%P;
p=p*p%P;
c=c>>1;
}
return ans%P;
}
int main(){
FO(bomb);
read(n),read(k);
for(ri i=0;i<=n;i++){
c[i][0]=1,g[i]=ksm(2,i*(i-1)/2);//printf("%d %d %lld\n",i,i*(i-1)/2,g[i]);
for(ri j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
}
for(ri i=1;i<=n;i++){
f[i]=g[i];
for(ri j=1;j<i;j++){
f[i]=(f[i]-(g[i-j]*c[i-1][j-1]%P*f[j])%P+P)%P;
}
//printf("%d %lld %lld\n",i,f[i],g[i]);
}
ff[0]=fff[0]=1;
for(ri i=1;i<=n;i++){
ff[i]=fff[i]=0;
for(ri j=1;j<=min(i,k);j++)ff[i]=(ff[i]+c[i-1][j-1]*f[j]%P*ff[i-j])%P;
for(ri j=1;j<=min(i,k-1);j++)fff[i]=(fff[i]+c[i-1][j-1]*f[j]%P*fff[i-j])%P;
}
printf("%lld\n",((ff[n]-fff[n])%P+P)%P);
return 0;
}
T3 queue
考虑使用分块在线处理,我们发现瓶颈是1操作,我们将在\([l,r]\)上的这个操作看成将r放到l位之前,然后所有都往后一位
于是我们可以将每一块设立两个指针指向块的左右端点,对于同一块内的操作暴力处理
对于\(l,r\)在不同块上的情况,一种naiive的做法是直接将\(r\)位的元素插入到\(l\)之前,但是这样可能会使块的大小不平衡,需要分裂重构比较麻烦
但是有个方法能避免这个问题
设\(l\)所在块为p,\(r\)所在块为q,我们把\(p\)到\(q-1\)中的每一块的末尾元素放到下一块的开头,这样的话就保持平衡了
我们可以将每一块设成一个\(deque\)(双端队列)来简洁地完成上述操作
/*
code by RyeCatcher
*/
const int maxn=100005;
const int maxb=355;
const int inf=0x7fffffff;
int n,m,size,k,a[maxn];
int L[maxn],R[maxn],pos[maxn];
deque <int> blo[maxb];
int cnt[maxn][maxb],sz[maxn];
int main(){
int x,y,l,r;
FO(queue);
freopen("queue2.in","r",stdin);
freopen("queue2.ans","w",stdout);
//DEBUG
//freopen("dat.in","r",stdin);
read(n),read(m);
if(!(n+m)){
return 0;
}
size=sqrt(n+0.5);
for(ri i=1;i<=size;i++)L[i]=(i-1)*size+1,R[i]=i*size;
if(R[size]<n)R[++size]=n,L[size]=R[size-1]+1;
for(ri i=1;i<=n;i++){
read(a[i]);
}
for(ri i=1;i<=size;i++){
for(ri j=L[i];j<=R[i];j++){
pos[j]=i;
sz[i]++;
cnt[a[j]][i]++;
blo[i].push_back(a[j]);
}
}
int opt,p,q;
deque<int>::iterator it;
while(m--){
read(opt),read(l),read(r);
p=pos[l],q=pos[r];
if(opt==1){
if(p==q){
x= *(blo[p].begin()+r-L[p]);
blo[p].erase(blo[p].begin()+r-L[p]);
blo[p].insert(blo[p].begin()+l-L[p],x);
}
else{
x= *(blo[q].begin()+r-L[q]);
//printf("--%d %d--\n",q,x);
blo[q].erase(blo[q].begin()+r-L[q]);
sz[q]--,cnt[x][q]--;
for(ri i=p;i<q;i++){
y= blo[i].back();
blo[i].pop_back();
cnt[y][i]--,sz[i]--,
blo[i+1].push_front(y);
cnt[y][i+1]++,sz[i+1]++;
}
blo[p].insert(blo[p].begin()+l-L[p],x);
sz[p]++,cnt[x][p]++;
}
}
if(opt==2){
read(k);
if(p==q){
x=0;
for(it=blo[p].begin()+l-L[p];it<=blo[p].begin()+r-L[p];it++){
if(*it==k)x++;
}
printf("%d\n",x);
}
else{
x=0;
for(it =blo[p].begin()+l-L[p];it!=blo[p].end();it++){
if(*it==k)x++;
//printf("%d ",*it);
}
for(it =blo[q].begin();it<=blo[q].begin()+r-L[q];it++){
if(*it==k)x++;
//printf("%d ",*it);
}
for(ri i=p+1;i<q;i++)x+=cnt[k][i];
printf("%d\n",x);
}
}
}
return 0;
}