zoukankan      html  css  js  c++  java
  • [LGP5241] 序列

    分析

    题目要求在一共加入若干条边是的不同B序列的个数;方便统计,我们不妨让一个B序列对应一种特定的操作来构造,具体如下

    从1开始按从小到大加入点,始终维护一个顺序为加点次序的链;每次加入一条有向边
    不会影响SCC个数的
    1)连上新点:链末尾与新点连
    2)连上废边:加上一条不改变强连通情况的边,不改变链
    (两个决策每次必须只选一种,钦定为能加点加点,否则考虑连废边)
    会影响的SCC个数
    3)把链上与1强连通的部分(钦定为链的前缀)向后扩展若干
    应注意到任意时刻SCC的数目为n-(j+1),n是图的点集大小,j是当前与1强连通的点集大小。

    这样的构造与B序列是一一对应的,于是我们可以考虑计数dp,设f[i,j,k]为已经加入了i条边,与1强连通的链前缀长度为j+1,除此之外还有长度k的不同的B序列的个数。
    转移和一些约束如下

    f[0,0,0]=1
    f[i,j,k]->f[i+1,j,k+1] j+1+k<n
    f[i,j,k]->f[i+1,j,k] j+1+k==n且j*(j+1)+k(k-1)/2+(j+1)*k>i (注意是有向边哦)
    f[i,j,k]->f[i+1,j+t,k-t] 0<t<=k 需要前缀和优化

    实现

    先留坑好了。 以下是暴力代码(80)

    #include <bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    
    inline void add(int&x,int y) {
    	if((x+=y)>=mod) x-=mod;
    }
    
    int n,now,nxt=1;
    int f[2][401][401],t[401][401];
    
    int main() {
    	scanf("%d",&n);
    	f[now][0][0]=1;
    	for(int i=0; i<n*(n-1); ++i,now=nxt,nxt^=1) {
    		for(int j=0; j<n; ++j) {
    			for(int k=0; j+k<n; ++k) {
    				if(!f[now][j][k]) continue;
    				if(j+1+k<n) add(f[nxt][j][k+1],f[now][j][k]);
    				else if(j*(j+1)+k*(k-1)/2+(j+1)*k>i) add(f[nxt][j][k],f[now][j][k]);
    				if(k>0) add(t[j+1][k-1],f[now][j][k]);
    				f[now][j][k]=0;
    			}
    		}
    		for(int j=0; j<n; ++j) {
    			for(int k=0; j+k<n; ++k) {
    				add(f[nxt][j][k],t[j][k]);
    				if(k>0) add(t[j+1][k-1],t[j][k]);
    				t[j][k]=0;
    			}
    		}
    		int ans=0;
    		for(int j=0; j<n; ++j) {
    			for(int k=0; j+k<n; ++k) {
    				add(ans,f[nxt][j][k]);
    			}
    		}
    		printf("%d ",ans);
    	}
    	return 0;
    }
    

    优化状态数目并吸氧的代码

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=400+5;
    const int mod=1e9+7;
    
    int n;
    int now,nxt=1,f[2][N][N],t[N][N];
    #define push(c,x,y) sta[c][++top[c]]=make_pair(x,y)
    int mx,top[2],tmp[N];
    pair<int,int> sta[2][20*N*N];
    bool vis[N];
    
    inline void add(int&x,int y) {
        if((x+=y)>=mod) x-=mod;
    }
    int main() {
        scanf("%d",&n);
        f[now][0][0]=1;
        push(now,0,0);
        for(int i=0; i<n*(n-1); ++i,now=nxt,nxt^=1) {
            top[nxt]=0;
            for(int p=1; p<=top[now]; ++p) {
                int j=sta[now][p].first;
                int k=sta[now][p].second;
    //			printf("|%d,%d>,",j,k); 
                if(!f[now][j][k]) continue;
                if(j+1+k<n) {
                    add(f[nxt][j][k+1],f[now][j][k]);
                    push(nxt,j,k+1);
                } else if(j*(j+1)+k*(k-1)/2+(j+1)*k>i) {
                    add(f[nxt][j][k],f[now][j][k]);
                    push(nxt,j,k);
                }
                if(k>0) {
                    add(t[j+1][k-1],f[now][j][k]);
                    vis[j+k]=true;
                }
                f[now][j][k]=0;
            }
            int c=0,pre=top[nxt];
            for(int s=0; s<n; ++s) if(vis[s]) {
                vis[s]=false; tmp[++c]=s;
            } 
            for(int j=0; j<n; ++j) {
                for(int p=c; p; --p) {
                    int k=tmp[p];
                    if(k<0) break;
                    add(f[nxt][j][k],t[j][k]);
                    if(f[nxt][j][k]) push(nxt,j,k);
                    if(k>0) add(t[j+1][k-1],t[j][k]);
                    t[j][k]=0; tmp[p]--;
                }
            }
            merge(sta[nxt]+1,sta[nxt]+pre+1,sta[nxt]+pre+1,sta[nxt]+top[nxt]+1,sta[nxt]+1); //归并
            top[nxt]=unique(sta[nxt]+1,sta[nxt]+top[nxt]+1)-sta[nxt]-1;
            int ans=0;
            for(int p=1; p<=top[nxt]; ++p) {
                add(ans,f[nxt][sta[nxt][p].first][sta[nxt][p].second]); 
            }
            printf("%d ",ans);
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    区间筛——模板
    I NEED A OFFER!
    dp入门(A)
    java如何创建类
    二维数组
    一维数组
    循环练习记录
    根据浏览器屏幕分辨率不同使用不同的css样式——响应式
    jquery 淡入淡出动画效果例子
    toggle()在新闻热点上的运用
  • 原文地址:https://www.cnblogs.com/nosta/p/10473655.html
Copyright © 2011-2022 走看看