arc100_d

题目叙述

求所有的长度为 \(n\) 的序列,每个位置的取值是 \([1,k]\) 内的整数,并且不存在连续 \(k\) 个数满足他们互不相同。求所有这样的序列,\(A_1\sim A_M\) 这个序列出现了多少次。

题解

“存在连续 \(k\) 个互不相同的”转化为“不存在连续 \(k\) 个互不相同的”。
只要总数去掉 \(A\) 在不存在连续 \(k\) 个互不相同的序列中出现的次数即可。
可以发现,如果 \(A\) 本身就有长度为 \(k\) 的连续段满足互不相同,这是容易计算的。
那么,剩下两种情况:

  1. \(A\) 中存在相同的数
  2. \(A\) 中不存在相同的数

先考虑第二种。
事实上,第二种情况的答案与 \(A\) 序列具体是什么并没有关系。因为是互不相同的情况。
所以其实就相当于统计所有序列中相邻 \(M\) 个互不相同的连续段的数量。
\(g_{i,j}\) 表示前 \(i\) 个,最后 \(j\) 个互不相同的方案数。
考虑讨论第 \(i-1\) 位往前一共有多少个互不相同的数。至少有 \(j-1\) 个互不相同的数。

  • 如果恰好 \(j-1\) 个互不相同的。那么从 \(g_{i-1,j-1}\) 转移。当前这一位有 \(k-(j-1)\) 中填法。
  • 否则 \(\ge j\) 个互不相同的数。那么一定第 \(i\) 个数和前面那个相同。当前这一位只有一种填法。

在此同时我们还需要计算前 \(i\) 个,最后 \(j\) 个互不相同的所有序列中连续 \(M\) 个互不相同的连续段数量总和为 \(f_{i,j}\)
最后再除掉 \(k^{\underline{M}}\) 就可以了。
对于第二种情况,考虑直接找到最长的连续不同的前缀和后缀,然后拼起来即可。

总结

  • 识别特征不仅需要直觉,还需要关键字。要对“存在”敏感。
  • 有的时候计算单个不好计算,可以补上一些相同的。就像arc100_d的做法,统计所有序列中 \(M\) 个互不相同的子串数量。

代码

#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(LL i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(LL i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const LL MN=1e5+5,MM=2e5+5;
const LL inf=2e14;
LL N,M,T,tote;
struct E{
	LL v,nxt;
	LL flw;
	E():v(0),nxt(0),flw(0){}
	E(LL _v,LL _nxt,LL _flw):v(_v),nxt(_nxt),flw(_flw){}
}edge[MM*5*2];
LL head[MM*2+MN+2];
void AddE(LL u,LL v,LL f){
	edge[++tote]=E(v,head[u],f);
	head[u]=tote;
}
void Add(LL u,LL v,LL f){
	AddE(u,v,f);
	AddE(v,u,0);
}
LL s,t;
LL dis[MM*2+MN+2];
LL cur[MM*2+MN+2];
bool bfs(){
	static const LL i_inf=1e9;
	FOR(i,1,t)dis[i]=i_inf,cur[i]=head[i];
	dis[s]=0;
	queue<LL> Q;
	Q.push(s);
	while(Q.size()){
		LL u=Q.front();
		Q.pop();
		for(LL p=head[u];p;p=edge[p].nxt){
			LL v=edge[p].v;
			if(edge[p].flw>0&&dis[v]>dis[u]+1){
				dis[v]=dis[u]+1;
				Q.push(v);
			}
		}
	}
	return dis[t]!=i_inf;
}
LL dfs(LL u,LL flw){
	if(!flw)return 0;
	if(u==t)return flw;
	LL res=flw;
	for(LL &p=cur[u];p;p=edge[p].nxt){
		LL v=edge[p].v;
		if(dis[v]!=dis[u]+1)continue;
		LL tmp=0;
		res-=(tmp=dfs(v,min(edge[p].flw,res)));
		edge[p].flw-=tmp;
		edge[p^1].flw+=tmp;
		if(!res)return flw;
	}
	return flw-res;
}
bool ss[MM*2+MN+2];
void dfs(int u){
	ss[u]=1;
	for(int p=head[u];p;p=edge[p].nxt){
		int v=edge[p].v;
		if(edge[p].flw>0&&(!ss[v])){
			dfs(v);
		}
	}
}
int main(){
	freopen("antibody.in","r",stdin);
	freopen("antibody.out","w",stdout);
	scanf("%lld%lld%lld",&N,&M,&T);
	tote=1;
	LL sum=0;
	s=N+2*M+1,t=N+2*M+2;
	FOR(i,1,M){
		LL u=0,v=0,w=0;
		scanf("%lld%lld%lld",&u,&v,&w);
		if(w<0)w=0;
		Add(N+M+i,N+i,inf);
		Add(v,N+M+i,inf);
		Add(N+i,u,inf);
		Add(s,N+i,w);
		Add(N+M+i,t,w);
		sum+=w;
	}
	LL ans=0;
	while(bfs())ans+=dfs(s,inf);
	ans=sum-ans;
	printf("%lld\n",ans);
	dfs(s);
	// set<int> s;
	// FOR(i,N+1,N+M)if(ss[i])cerr<<i-N<<" ";
	// cerr<<endl;
	// FOR(i,N+M+1,N+M+M)if(!ss[i])cerr<<i-N-M<<" ";
	// cerr<<endl;
	static int create[MM],totc;
	FOR(i,N+1,N+M)if(ss[i]&&(!ss[i+M]))create[++totc]=i-N;
	printf("%d\n",totc);
	FOR(i,1,totc)printf("%d ",create[i]);
	// printf("\n");
	// system("grep VmPeak /proc/$PPID/status >/dev/tty");
	fclose(stdin);
	fclose(stdout);
	return 0;
}
```cpp
posted @ 2022-07-07 10:22  YouthRhythm  阅读(59)  评论(0)    收藏  举报