组合数习题总结(未完。。。)

Problem A——HDU:2067 小兔的棋盘(dp/卡兰特数)

http://acm.hdu.edu.cn/showproblem.php?pid=2067

题意:从棋盘(0,0)点到(x,y)点有几种走法,要求不能穿过棋盘对角线。

分析:

角度一:DP,已知经典棋盘问题状态转移方程:dp[i][j]=dp[i-1][j]+dp[i][j-1],而本题由于不能穿过棋盘的对角线,因此可以考虑将棋盘划分为两个部分,ans=ans1+ans2=ans1*2,同时在上述状态转移方程的基础上特判棋盘对角线上的点,即if(i==j)dp[i][j]=dp[i-1][j],最后求出dp[n][n]再乘以2即可。

角度二:卡兰特数公式(但本题用公式二的话会溢出)

Problem D——HDU 4135 Co-prime(容斥原理 + 基础数论)

题意:给定一个区间A~B,问A~B中与N互质的数有多少。

分析:

初见本题,最先想到的是欧拉函数,不过好像真的没什么关系。。。

抽象出基本模型:1~A中与N互质的数有多少。(N可能大于A,也可能小于A)。

解决思路:转换为求1~A中与N互合的数,若x与N“互合”,则两者有某一公因子,因此考虑将N分解素因子(合数都可以由素数乘积得到,所以不是分解“合”因子),也就是说N的素因子通过组合可以得到N的所有因子,令集合A为1~A中可被素因子①整除的数的集合,B为素因子②......,则1~A中与N互合的数的集合为A\cup B\cup C......,然后容斥定理求该集合中数的个数,最后的1~A中与N互质的数的个数为A-\left | A\cup B\cup C... \right |(前面A指的是一个数,后面是集合,不要在意细节...)。

扫描二维码关注公众号,回复: 2803468 查看本文章

然后该种题型总结为模板的话有(二进制枚举法求互合数个数):

https://blog.csdn.net/qq_41661919/article/details/81677108

Problem E——HDU 1796How many integers can you find(容斥定理)

题意:给定一个因子集合M,一个数N,问1~N-1中为M中元素的倍数的数的个数。

分析:(疑问:为什么不能保留o,之后在判断?????)

          和上道题较相似,因子已经分解好了,只要二进制枚举因子然后统计个数就好了,需要注意的是M中元素并不是互质的,因此1~N-1中某数x与M中某一元素的公因子不一定是M1*M2的形式,而应该是LCM(M1,M2)LCM(M1,M2)的形式,所以上述模板中求几个数的因子积的地方要修改为求几个数的LCM的形式,AC代码自己去杭电找提交记录吧(PS:该题坑点,要注意去掉零)。

Problem H——HDU 1695 GCD(容斥原理+欧拉phi函数)

题意:给定两个区间,1~b,1~d,分别从中取一个数:x,y,要求GCD(x,y)=某个数K,问满足要求的x,y对有多少组。

分析:

处理已知等式得到:

GCD(x,y)=K,(1\leqslant x\leqslant b,1\leqslant y \leqslant d) \Rightarrow GCD({x}',{y}')==1,(1\leqslant x\leqslant b/k,1\leqslant y \leqslant d/k)

因此可以枚举某一区间内所有元素,判断另一区间内与该确定数互质的数的 个数——容斥定理二进制枚举+质因子分解。

另外,为了保证不会出现(a,b)and(b,a)这种重复的情况,我们可以只统计a<b的情况,于是有如下处理技巧:将1~d/k分为1~b/k和b/k+1~d/k两部分,(假设d>b),然后分别求解,前半区间的话容易发现,直接欧拉函数即可,后半区间则套用前面几题的容斥+素因子法即可。

PS:求单个数欧拉函数值模板:https://blog.csdn.net/qq_41661919/article/details/81677207

操。。。写了一下午。。。没了。。。算了。。。不补了,弄明白了就好了,反正也是一些水题。。。

Problem L—— HDU5648 Puzzled Elena(dfs序+容斥)

题意:

给一颗根节点为1,有n个节点,n-1条边的树,问某一节点的子树中与该节点权值互质的节点的个数。

分析:

相比于前面做到过的容斥基础题,该题的变化在于1~r中的元素并不是连续的,而是一个离散的集合,不过原理都是一样的。

首先我们考虑求与节点互质数的个数可以转换为求互合点的个数。那么必然,我们首先要分解质因子,因为这里要用到的质因子较多,所以我们做一个预处理多分解几个数(1~10^5),然后就是奇加偶减的部分怎么处理了,对于已得到的一个因子,我们通过一个数组存储当前搜过的所有点中有该一因子的节点的个数,那个按照dfs搜索顺序,当再次回溯到该节点时我们可以得到该节点子树中具有该因子的数的个数,也就是与该节点互合的部分数的个数。

最后:这题的细节很不好处理,限时的话用该不容易,呃。。。复杂点的dfs都不好调。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include <cassert>
#include<ctime>
#define ll long long
#define INF 0x7fffffff
using namespace std;
vector <ll> v[1000010];
vector<ll>fac[1000010];
ll a[1000100];
const ll MAX=100001;
bool mark[MAX+100];
ll ans[MAX+100];
ll b[MAX+100];
void init()
{
    memset(mark,0,sizeof(mark));
    mark[1]=mark[0]=1;
    for(ll i=0;i<=MAX;++i)fac[i].clear();
    for(ll i=2;i<=MAX;++i)
    {
        if(mark[i]==0)
            for(ll j=i;j<=MAX;j+=i)
            {
                fac[j].push_back(i);
                mark[j]=1;
            }
    }
}

ll solve(ll n,ll x)
{
    ll sum=0;
    for(ll i=1;i<(1<<(fac[n].size()));++i)
    {

        ll mul=1,num=0;
        for(ll j=0;j<fac[n].size();++j)
        {
            if((i>>j)&1)
            {
                num++;
                mul*=fac[n][j];
            }
        }
        if(num&1)sum+=b[mul];
        else sum-=b[mul];
        b[mul]+=x;
    }
    return sum;
}

ll dfs(ll x,ll pre)
{
    ll tot=solve(a[x],1);
    ll num=0;
    for(ll i=0;i<v[x].size();++i)
    {
        if(v[x][i]==pre)continue;
        num+=dfs(v[x][i],x);
    }
    tot=solve(a[x],0)-tot;
    tot=num+1-tot;
    ans[x]=tot;
    return num+1;
}
int main()
{

    ll n;
    init();
    int k=0;
    while(scanf("%lld",&n)!=EOF)
    {
        memset(ans,0,sizeof(ans));
        memset(b,0,sizeof(b));
        for(ll i=0;i<=MAX;++i)v[i].clear();
        for(ll i=1;i<=n-1;++i)
        {
            ll x,y;
            scanf("%lld %lld",&x,&y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        for(ll i=1;i<=n;++i)scanf("%lld",&a[i]);
        dfs(1,0);
        printf("Case #%lld:",++k);
        for(ll i=1;i<=n;++i)printf(" %lld",ans[i]);
        printf("\n");
    }
    return 0;

}

Problem M——POJ 3256 Cow Picnic

题意:

一张n个点的无向图,k头奶牛分布在图中位置已知,各点之间的连接关系已知。问满足所有牛都可以到达的点有几个。

分析:

猜你喜欢

转载自blog.csdn.net/qq_41661919/article/details/81676549