首页 > 其他分享 >【XSY3405】零糖麦片(二分图,复杂度均衡)

【XSY3405】零糖麦片(二分图,复杂度均衡)

时间:2022-10-30 12:34:22浏览次数:57  
标签:frac int 复杂度 cdots 零糖 ans mod define XSY3405

一个听说很套路但我不会的套路:对于一个非 \(1\) 数 \(w_i\),把它看成是 \((w_i-1)+1\),于是原式变为:

\[ans=\sum_{e_1,\cdots,e_t}(n-t)!\prod_{i=1}^{t}(w_{e_i}-1) \]

其中 \(\{e_1,\cdots,e_t\}\) 是 \(\{1,\cdots ,k\}\) 的一个子集,且满足 \(x_{e_1},\cdots.x_{e_t}\) 互不相同、\(y_{e_1},\cdots,y_{e_t}\) 互不相同,其实也就是枚举一种满足条件非 \(1\) 数的选法。

然后暴力枚举是 \(O(k\cdot 2^k)\) 的。

转化成二分图:在 \(x_i\) 与 \(y_i\) 之间连一条边权为 \(w_i-1\) 的边,那么我们就是要对 \(i=0,\cdots,k\) 求出选了 \(i\) 条边的匹配的价值和,其中一种匹配的价值为所有选了的边乘起来。

考虑枚举所有连通块再合并,发现一个连通块内至多有 \(k\) 条边,那么至多有 \(k+1\) 个点,那么肯定有一侧至多有 \(\lfloor\frac{k+1}{2}\rfloor\) 个点,于是我们可以状压这一侧的点,一个一个地加入另一侧的点进行 DP。时间复杂度 \(O(k^2\cdot 2^{\frac{k}{2}})\)。

实际上这个做法加一些剪枝就可以过了。

更加优秀的做法:

仍然枚举连通块再合并,设当前连通块点数为 \(s\)。若 \(s\leq \frac{2}{3}k\),我们沿用刚刚的做法;若 \(s>\frac{2k}{3}\),那么我们随便找出一个这张图的 dfs 树,显然非树边至多 \(\frac{k}{3}\) 条,那么我们可以暴力枚举非树边的选择状态,然后对于每一种状态做树形 DP。时间复杂度 \(O(k^2\cdot 2^{\frac{k}{3}})\)。

用的是 \(O(k^2\cdot 2^{\frac{k}{2}})\) 做法+剪枝:

#include<bits/stdc++.h>

#define K 55
#define N 100010
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)

using namespace std;

namespace modular
{
    const int mod=1000000007;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int mul(int x,int y){return 1ll*x*y%mod;}
    inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
}using namespace modular;

inline int poww(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}

int n,k;
int fac[N];
int fa[N<<1];
int popc[33554440];
int ans[K];

vector<pii>e[N<<1];
vector<int>pl[N<<1],pr[N<<1];

int find(int x)
{
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}

map<int,int>dp[2][K];

void work1(int rt)
{
    static int id[N<<1],sta[K];
    if(pr[rt].size()<pl[rt].size()) swap(pl[rt],pr[rt]);
    int idx=0;
    for(int u:pl[rt]) id[u]=idx++;
    sta[pr[rt].size()]=0;
    for(int i=pr[rt].size()-1;i>=0;i--)
    {
    	sta[i]=sta[i+1];
    	int u=pr[rt][i];
    	for(pii edge:e[u])
    	{
    		int v=id[edge.fi];
    		sta[i]|=(1<<v);
		}
	}
    //sta[i]用于剪枝优化
    bool pre=0,now=1;
    dp[now][0].clear();
    dp[now][0][0]=1;
	for(int i=0,s=pr[rt].size();i<s;i++)
    {
    	int u=pr[rt][i];
        swap(pre,now);
        for(int j=0;j<=i+1;j++) dp[now][j].clear();
        for(int j=0;j<=i;j++)
        {
	        for(auto tmp:dp[pre][j])
	        {
	            int s=tmp.fi;
	            Add(dp[now][j][s&sta[i+1]],tmp.se);
	            for(pii edge:e[u])
	            {
	                int v=id[edge.fi];
	                if(!((s>>v)&1)) Add(dp[now][j+1][(s^(1<<v))&sta[i+1]],mul(tmp.se,dec(edge.se,1)));
	            }
	        }
		}
    }
    
    for(int i=k;i>=0;i--)
        for(int j=1;j<=pl[rt].size()&&i-j>=0;j++)
            Add(ans[i],mul(ans[i-j],dp[now][j][0]));
}

int main()
{
    n=read(),k=read();
    for(int i=1,t=(1<<25);i<t;i++) popc[i]=popc[i>>1]+(i&1);
    for(int i=1;i<=(n<<1);i++) fa[i]=i;
    for(int i=1;i<=k;i++)
    {
        int u=read(),v=n+read(),w=read();
        e[u].push_back(pii(v,w));
        e[v].push_back(pii(u,w));
        int a=find(u),b=find(v);
        assert(a<=n);
        if(a!=b) fa[b]=a;
    }
    for(int i=1;i<=n;i++) pl[find(i)].push_back(i);
    for(int i=n+1;i<=(n<<1);i++) pr[find(i)].push_back(i);
    ans[0]=1;
    for(int i=1;i<=n;i++)
        if(find(i)==i&&pr[i].size()) work1(i);
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);
    int Ans=0;
    for(int i=0;i<=k;i++)
        Add(Ans,mul(fac[n-i],ans[i]));
    printf("%d\n",Ans);
    return 0;
}
/*
3 1
1 1 2
*/
/*
10 10
3 3 367056794
6 2 124561273
1 3 46718146
6 9 415916869
10 5 985968336
3 1 526792265
1 4 386357058
10 4 349304187
2 7 102032499
3 6 502679075
*/

标签:frac,int,复杂度,cdots,零糖,ans,mod,define,XSY3405
From: https://www.cnblogs.com/ez-lcw/p/16840977.html

相关文章

  • 【XSY3326】米缸(时间复杂度均衡,线段树,基环树,倍增)
    时间复杂度的均衡。先考虑暴力的想法:显然这是一棵基环树,那么我们每次修改时暴力\(O(nm)\)重构基环树,然后询问的时候就能\(O(1)\)查询。时间复杂度\(O(nmq)\)。考虑......
  • 时间复杂度
    时间复杂度概述在恒定的环境内,他的执行次数和对应的变量的比列构成的值为时间复杂度。时间复杂度是在一定程度上表示当前的程序的运行速度,时间复杂度越低那么运行速度就越......
  • 排序算法(常见的排序算法的时间复杂度 O(n2))
    排序算法(常见的排序算法的时间复杂度O(n2))1.冒泡排序(俩俩(相邻的俩个)相比位置交换)O(n2)```js//冒泡排序functionbubleSort(arr){//冒泡排序外层的轮数......
  • 洛谷 P5698 [CTSC1998]算法复杂度 题解
    前言咕了大半年,我回来了说实话当鸽子的感觉挺不错???原题链接题意给定一个伪代码,判断他总共需要进行几次操作,用多项式形式输出。题解首先,这是一道模拟题,发现性质的话比......
  • 算法与算法分析—时间复杂度、空间复杂度
    算法与算法分析算法的定义对特定问题求解方法和步骤的一种描述,它是指令的有限序列。其中每个指令表示一个或多个操作。算法的描述自然语言:英语、中文(麻烦)流......
  • 辗转相除的时间复杂度
    \(\gcd(a,b)=\gcd(b,a\%b)\)这是辗转相除法,也叫欧几里得算法欧几里得算法的时间复杂度我们认为是\(O(logn)\)的。证法1设\(a>b\)分为两种情况:①\(a>2b\)发......
  • 【数据结构】时间复杂度与空间复杂度概念
    1.时间复杂度1.什么是时间复杂度时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算......
  • 算法复杂度,本机1s用例测试
    本机配置:MacBookPro2019CPU:2.4GHz四核IntelCorei5第一个用例:时间复杂度O(n)//O(n)voidfunction1(longlongn){longlongk=0;for(longlong......
  • 时间复杂度和空间复杂度
    写在前面说实话,我已经学了不少的数据结构了,第一次接触时间和空间复杂度的时候感觉有些不好理解,这篇博客就搁置下来了.这些天感觉自己思路理清楚了.所以开始和大家分享.要......
  • 数据结构—算法的时间复杂度
    1、什么是时间复杂度     一般情况下,算法中基本语句重复执行的次数是问题规模n的某个函数f(n),算法的时间量度记作T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时......