Problem A——HDU:2067 小兔的棋盘(dp/卡兰特数)
http://acm.hdu.edu.cn/showproblem.php?pid=2067
题意:从棋盘(0,0)点到(x,y)点有几种走法,要求不能穿过棋盘对角线。
分析:
角度一:DP,已知经典棋盘问题状态转移方程:,而本题由于不能穿过棋盘的对角线,因此可以考虑将棋盘划分为两个部分,,同时在上述状态转移方程的基础上特判棋盘对角线上的点,即,最后求出再乘以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互合的数的集合为,然后容斥定理求该集合中数的个数,最后的1~A中与N互质的数的个数为(前面A指的是一个数,后面是集合,不要在意细节...)。
然后该种题型总结为模板的话有(二进制枚举法求互合数个数):
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中某一元素的公因子不一定是的形式,而应该是的形式,所以上述模板中求几个数的因子积的地方要修改为求几个数的LCM的形式,AC代码自己去杭电找提交记录吧(PS:该题坑点,要注意去掉零)。
Problem H——HDU 1695 GCD(容斥原理+欧拉phi函数)
题意:给定两个区间,1~b,1~d,分别从中取一个数:x,y,要求GCD(x,y)=某个数K,问满足要求的x,y对有多少组。
分析:
处理已知等式得到:
因此可以枚举某一区间内所有元素,判断另一区间内与该确定数互质的数的 个数——容斥定理二进制枚举+质因子分解。
另外,为了保证不会出现(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头奶牛分布在图中位置已知,各点之间的连接关系已知。问满足所有牛都可以到达的点有几个。
分析:
图的可达点问题,深搜、广搜、最短路均可以。
用深搜实现思路:
遍历牛当前所在的位置,以此为起点搜索,搜到的点(vector模拟邻接表、二维数组都行)标记数组权值加一,最后找权值==牛的数目的点即可。然后注意dfs求可达点回溯时标记过的点是不需要取消标记的。
然后这题其实正解应该用bfs,效率也更高。
Problem N——POJ 2249 Binomial Showdown
题意:求组合数。
分析:题目挺简单的,但是这题卡空间。
n,m的范维应该不小,所以一般的递推公式开二维数组是装不下的。
然后考虑第二个常用公式。。。是可以过的,且空间大小仅决定于m的大小,但注意乘除的顺序没弄错了,另外memset会占用很多空间么?怎么加上以后就爆空间了。
最后,我们也可以对阶乘公式做一下小处理,直接乘的话肯定会爆精度,所以一个数组存分子,一个存分母,约分不就好了,最后分母一定能变为1。
。
所以总结:求组合数时,两个递推式一般卡空间,且二维数组公式形式更容易爆炸,阶乘式的话又卡空间还卡时间。。。
PS:阶乘式的话这题用高精度模板能过么?好吧,百交不A,Time limit。。。