[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;
}

posted @ 2018-10-23 20:22  Rye_Catcher  阅读(216)  评论(1编辑  收藏  举报