zoukankan      html  css  js  c++  java
  • 【JZOJ3799】青蛙神

    题目

    题目链接:https://jzoj.net/senior/#main/show/3799

    思路

    如果一个数是完全平方数,当且仅当它分解质因数后,每一个质因数的质数均为偶数。
    由于\(n\leq 90\),所以最多有\(24\)个质数。考虑状压。
    所以我们预处理出\(prmS[x]\)表示数字\(x\)分解质因数后,指数为奇数的质因数集合。
    \(f[x][S]\)表示处理到\(DAG\)中的点\(x\),终点为\(x\)的所有路径中,有多少条指数为奇数的质数集合为\(S\)的路径。
    由于是一张\(DAG\),所以不用担心一条路径走过一条边两次。
    那么对于\(DAG\)中的每一条边\((y,x)\),我们有

    \[f[x][S\ xor\ prmS[x]]=\sum^{(y,x)}_{y}f[y][S] \]

    答案是\(\sum^{n}_{i=1}f[i][0]\)
    这个算法的时空复杂度均为\(O(n2^m)\),空间会炸。
    我们发现,其实所有大于\(\frac{90}{2}\)的质数都是没有用的,因为在\(1\sim n\)中,含有该质因子的数就只有这个质数本身,所以如果一条路径包含这个点,那么这条路径必然会有一个质因子的质数为1。
    也就是说,大于\(\frac{90}{2}\)的质数我们都可以根本不用在图中出现,直接删去这个点即可,因为这个点不会造成任何贡献。
    那么有用的质因数就只有\(2,3,5,7,11,13,17,19,23,29,31,37,41,43\)\(14\)个了。
    这样时空复杂度就降到了\(O(n2^{14})\),可以过掉本题。

    代码

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N=100,M=8010,MAXN=(1<<14);
    const int prime[15]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43};
    int n,m,tot,head[N],prmS[N],in[N];
    ll ans,f[N][MAXN];
    
    struct edge
    {
    	int next,to;
    }e[M];
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    void topsort()
    {
    	queue<int> q;
    	for (int i=1;i<=n;i++)
    		if (!in[i]) q.push(i);
    	for (int i=1;i<=n;i++)
    		f[i][prmS[i]]=1;
    	while (q.size())
    	{
    		int u=q.front();
    		q.pop();
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			for (int j=0;j<MAXN;j++)
    				f[v][j^prmS[v]]+=f[u][j];
    			in[v]--;
    			if (!in[v]) q.push(v);
    		}
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		if (x==47||x==53||x==59||x==61||x==67||x==71||x==73||x==79||x==83||x==89) continue;
    		if (y==47||y==53||y==59||y==61||y==67||y==71||y==73||y==79||y==83||y==89) continue;
    		add(x,y); in[y]++;
    	}
    	for (int i=2;i<=n;i++)
    		for (int p=i,j=1;prime[j]<=p && j<=14;j++)
    			if (!(p%prime[j]))
    			{
    				bool cnt=0;
    				for (;!(p%prime[j]);p/=prime[j]) cnt^=1;
    				if (cnt) prmS[i]|=(1<<j-1);
    			}
    	topsort();
    	for (int i=1;i<=n;i++)
    		if (i!=47&&i!=53&&i!=59&&i!=61&&i!=67&&i!=71&&i!=73&&i!=79&&i!=83&&i!=89)
    			ans+=f[i][0];
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    与WinRT组件进行操作
    clr via c# 运行时序列化
    clr via c# 程序集加载和反射(2)
    clr via c# 程序集加载和反射集(一)
    clr via c# clr寄宿和AppDomain (一)
    cir from c# 托管堆和垃圾回收
    浏览器页面的生命周期
    C#常用泛型集合
    Params修饰符
    ASP.net应用程序的生命周期
  • 原文地址:https://www.cnblogs.com/stoorz/p/12111312.html
Copyright © 2011-2022 走看看