概述
Description
给出n个数,每个数是0或1.
再给出m个限制,每个限制(x,y)表示“前x个数中有y个1”或“后y个数中有x个1”
求这样的序列的个数。
n<=5000,m<=1000
Solution
再一次被UER给虐了。
其实这道题劼鏼爷已经讲的很清楚了。(扑通扑通跪下来)
当x!=y的时候,很显然已经确定这个限制是限制前缀还是后缀的。
当x=y的时候,我们只需要保留最大的那个x就行了。(显然)
然后,我们可以用容斥。
答案是x=y是前缀限制的个数+后缀限制的个数-前后缀都限制的个数。
那么,现在就是有一堆前后缀的限制,让你求方案数。
如果只有前(后)缀,那么,就相当于把这n个数划分成了m个区间,对于每一个区间单独限制。一堆组合数乘起来就好了。
那么,前后缀都有的话,我们就可以把后缀转化成前缀。(反之亦然)
枚举1的总和就行了。
劼鏼大法好!(刷队形)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 5005
using namespace std;
typedef long long ll;
const int mo=998244353;
struct note{int x,y,bz,ty;}a[N],p[N];
bool cmp(note x,note y) {return x.x<y.x;}
int c[N][N],n,m,ty,x,y,z,ans,tot;
int main() {
c[0][0]=1;
fo(i,1,N-5) {
c[i][0]=1;
fo(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
for(scanf("%d",&ty);ty;ty--) {
scanf("%d%d",&n,&m);tot=z=ans=0;
fo(i,1,m) {
scanf("%d%d",&x,&y);
if (x>y) a[++tot].x=x,a[tot].y=y,a[tot].bz=0,a[tot].ty=0;
else if (x<y) a[++tot].x=n-y,a[tot].y=x,a[tot].bz=1,a[tot].ty=0;
else z=max(z,x);
}
a[++tot].x=z;a[tot].y=z;a[tot].bz=0;a[tot].ty=1;
a[++tot].x=n-z;a[tot].y=z;a[tot].bz=1;a[tot].ty=2;
sort(a+1,a+tot+1,cmp);
fo(type,1,3)
fo(i,1,n) {
int cnt=0;bool pd=0;
fo(j,1,tot) {
if (a[j].ty==type) continue;
p[++cnt].x=a[j].x;
if (a[j].bz) p[cnt].y=i-a[j].y;
else p[cnt].y=a[j].y;
if (p[cnt].y<0||p[cnt].y>i) {pd=1;break;}
if (p[cnt].x<p[cnt].y) {pd=1;break;}
if (p[cnt].x-p[cnt-1].x<p[cnt].y-p[cnt-1].y) {pd=1;break;}
if (p[cnt].y-p[cnt-1].y<0) {pd=1;break;}
}
if (pd) continue;int sum=1;
fo(j,1,cnt) sum=(ll)sum*c[p[j].x-p[j-1].x][p[j].y-p[j-1].y]%mo;
sum=(ll)sum*c[n-p[cnt].x][i-p[cnt].y]%mo;
if (type<=2) ans=(ans+sum)%mo;
else ans=(ans-sum+mo)%mo;
}
printf("%dn",(ans%mo+mo)%mo);
}
}
最后
以上就是无语小兔子为你收集整理的[uoj#209][UER#6A]票数统计的全部内容,希望文章能够帮你解决[uoj#209][UER#6A]票数统计所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复