目录:
一、计算“闰日”数量
二、求两个矩形的面积并
三、求小于n的数中有多少个数可以被已知集合中的数整除
四、给你A、B、N,让你求出[A,B]区间中与N互质的数字个数
五、求在n*m的棋盘中放置k个棋子的方法,要求棋盘四边都有棋子
六、跳蚤
。
。
一、计算“闰日”数量
题源:LightOJ1414
思路:判断好区间端点 然后做简单容斥处理即可
1、如何判断闰年:
if((y%4==0&&y%100!=0)||y%400==0) return true;
else return false;
2、如何处理区间端点:
若左端点是闰年,且左端点向右不包含闰日 则左端点年份++
若右端点是闰年,且右端点向左不包含闰日 则右端点年份- -
则问题转化为求区间内闰年数。
3、如何统计 0~当前年闰日数?
容斥原理前缀和思想。
ans=y/4+y/400-y/100;//你品 你细品
最后上代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#define ll long long
#define cl(a,b) memset(a,b,sizeof(a))
using namespace std;
map<string,int> mp;
void init() {
mp["January"]=1,mp["February"]=2,mp["March"]=3;
mp["April"]=4,mp["May"]=5,mp["June"]=6;
mp["July"]=7,mp["August"]=8,mp["September"]=9;
mp["October"]=10,mp["November"]=11,mp["December"]=12;
}
bool judge(int y) { //判断是否为闰年
if(y%4==0&&y%100!=0||y%400==0) return true;
return false;
}
int calculate(int year1,int year2) { //计算这两年(闭区间里的闰年数量
year1--;//用了差分的知识
int ans=(year2/400+year2/4-year2/100);
ans-=(year1/400+year1/4-year1/100);
return ans;
}
int main() {
init();
int T;
scanf("%d",&T);
for(int k=1;k<=T;k++){
int ans;
char month1[30],month2[30];
int day1,day2,year1,year2;
scanf("%s%d,%d",month1,&day1,&year1);
scanf("%s%d,%d",month2,&day2,&year2);
if(judge(year1)&&mp[month1]>=3) year1++;
if(judge(year2)&&(mp[month2]==1||(mp[month2]==2&&day2<=28))) year2--;
ans=calculate(year1,year2);
printf("Case %d: %d\n",k,ans);
}
return 0;
}
二、求两个矩形的面积并
题源:51Nod2488
思路:就像集合一样,A∪B=A+B-A∩B
重点是怎么求A∩B
直接给代码就能明白了 这个题不多说
#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
#define inf 0x3f3f3f3f
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg printf("aaa\n")
using namespace std;
//求矩形面积并
//嗷嗷这题很简单啊但是自己非要穷举 我真是服了- -
int cal(int a,int b,int c,int d){
if(c>=b||a>=d) return 0;
return min(b,d)-max(a,c);
}
int main() {
int a,b,c,d,e,f,g,h;
cin>>a>>b>>c>>d>>e>>f>>g>>h;
cout<<(c-a)*(d-b)+(g-e)*(h-f)-cal(a,c,e,g)*cal(b,d,f,h)<<endl;
//system("pause");
return 0;
}
三、求小于n的数中有多少个数可以(还有题是求不可以)被已知集合中的数整除
题源:HDU1796
题意上面一行写的很明确了,可以看出是容斥。为什么?
因为1~n中的某个数,可能被已知集合中的1个或者2个或者多个数的乘积同时整除,所以我们要 加上(n-1)/奇数个元素的最小公倍数 减去(n-1)/偶数个元素的最小公倍数 最后得到的 结果 就是答案。
问:为什么是最小公倍数而不是集合的数直接相乘?答:因为集合中的数不一定互质,他们的最小公倍数才最合适,而若是把一个大数分解成了质因数(如下题),则就直接用乘积即可!!!!!
若想求1~n中与集合互质的个数呢?只要用n-1减去结果 就是答案 例题:UVA10325
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define cl(a,b) memset(a,b,sizeof(a))
using namespace std;
//HDU1796
int n,m;
ll num[30];
ll cnt,ans;
ll gcd(ll a,ll b){
if(b==0) return a;
return gcd(b,a%b);
}
ll Lcm(ll a,ll b) {
return a/gcd(a,b)*b;
}
void dfs(int p,ll lcm,int depth) {
if(depth&1) ans+=(n-1)/lcm;
else ans-=(n-1)/lcm;
for(int i=p+1; i<=cnt; i++) {
ll lcm2=Lcm(lcm,num[i]);
dfs(i,lcm2,depth+1);
}
return;
}
int main() {
while(cin>>n>>m) {
ans=0;
cnt=0;
cl(num,0);
for(int i=1; i<=m; i++) {
int temp;
cin>>temp;
if(temp) num[++cnt]=temp;
}
for(int i=1; i<=cnt; i++) {
dfs(i,num[i],1);
}
cout<<ans<<endl;
}
return 0;
}
四、给你A、B、N,让你求出[A,B]区间中与N互质的数字个数
题源:HDU4135
首先:看到与N互质,想到1~N-1 中与N互质个数,那首先想到欧拉函数!
什么是欧拉函数呢,简言之 phi(n)的值为1~n中与数n 互质的数字个数。
但是这里不是求1~N-1中 而是给定区间A B。
那我们需要怎么求呢?
思路:
1、求出N的所有质因子,存入fac数组中。下面是实现代码:
void get_fac(int n){//求出1~n的质因子 适用于 n数量不是很多 但是n很大 的情况
//若n数量很多 但是n普遍不大 则适用埃氏筛的方法 顺便求出每个数的质因子
cnt=0;
for(int i=2;i*i<=n;i++){
if(n%i==0){
fac[++cnt]=i;
while(n%i==0) n/=i;
}
}
if(n>1) fac[++cnt]=n;//这句很重要 不知道为什么
return;
}
2、利用前缀和/容斥原理的思想,求出1到A-1中、1到B中 与N不互斥的数量,注意是不互斥,互斥的数量分别用总数减去即可。
3、求2中不互斥数量的方法是,奇数个相乘的时候 加上up/mul 其中up是上界 即A-1或者B,偶数个相乘的时候 减去up/mul 注意这里直接是乘积mul因为相当于上题集合中的数 都互质了 所以乘积就已经是最小公倍数。
4、最后分别用总数减去互斥数量 相减即得最终答案。
下面是AC代码,who 就是fac质因子数组:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define cl(a,b) memset(a,b,sizeof(a))
using namespace std;
int n,cnt;//因子个数
ll a,b,ans;//记录结果
ll who[1010];//记录质因子
void get_who(int n){//求出1~n的质因子
cnt=0;
for(int i=2;i*i<=n;i++){
if(n%i==0){
who[++cnt]=i;
while(n%i==0) n/=i;
}
}
if(n>1) who[++cnt]=n;//这句很重要 不知道为什么
return;
}
void dfs(int p,int num,ll mul,ll up){//p 当前到哪一个了 num 当前一共有多少个 mul 当前乘到多少了
//奇数个相乘的时候 加上 偶数个相乘 减去
if(num&1) ans+=up/mul;
else ans-=up/mul;
for(int i=p+1;i<=cnt;i++){
dfs(i,num+1,mul*who[i],up);
}
}
int main() {
int T;
cin>>T;
for(int t=1;t<=T;t++){
cin>>a>>b>>n;
//先求出n的所有质因子存到num里
get_who(n);
//先求简单的 用搜索 求1~a中n的因子 倍数 的个数
ans=0;
for(int i=1;i<=cnt;i++){
dfs(i,1,who[i],a-1);
}
ll aa=ans;
ans=0;
for(int i=1;i<=cnt;i++){
dfs(i,1,who[i],b);
}
ll bb=ans;
//现在 aa bb 中存的是1~a-1 1~b中 和n不互质的数量
ll res=b-bb-(a-1-aa);
cout<<"Case #"<<t<<": "<<res<<endl;
}
return 0;
}
五、求在n*m的棋盘中放置k个棋子的方法,要求棋盘四边都有棋子
题源:UVA11806
思路:第一反应显然组合数,但是不会做1551。。。
1、根据m n 先处理出组合数。注意c[n][m]=c[n-1][m]+c[n-1][m-1]
2、根据容斥原理 枚举状态 0000到1111(0到15)代表第一行、第一列、最后一行、最后一列有没有棋子,若有偶数个就加上,奇数个就减去。
3、对于上一条的解释:刚开始没有棋子,0000 所以n 和 m都不会减少 从中选k个 这样 c n*m k 是最大的 然后减去一个的 加上两个的 减去三个的 加上四个的
至于第三条为什么这样 自己还是搞不明白。
下面是AC(copy着写的)代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define MID (t[k].l+t[k].r)>>1
#define cl(a,b) memset(a,b,sizeof(a))
#define dbg printf("aaa\n")
using namespace std;
//这是一个容斥问题 可用状压枚举状态
const int maxn=510;//从所有中选出最多五百种嘛
const int mod=1000007;
int n,m,k;
int c[maxn][maxn];
void get_c(){
//注意公式 c n m= c n-1 m + c n-1 m-1 头一次听说来!!!
cl(c,0);
for(int i=0;i<=maxn;i++){//至少从0个开始取吧
c[i][0]=c[i][i]=1;
for(int j=0;j<i;j++){//c i i 已经初始化了
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
return;
}
int main() {
get_c();
int T;
scanf("%d",&T);
for(int x=1;x<=T;x++){
scanf("%d%d%d",&n,&m,&k);
int sum=0;
for(int i=0;i<16;i++){//枚举16种状态 0000~1111 0~15
//代表第一行、最后一行、第一列、最后一列有无棋子
int a=n,b=m,cnt=0;
if(i&1){
--a,++cnt;
}if(i&(1<<1)){
--a,++cnt;
}if(i&(1<<2)){
--b,++cnt;
}if(i&(1<<3)){
--b,++cnt;
}
if(cnt&1){//若奇数个
sum=(sum-c[a*b][k]+mod)%mod;
}else{
sum=(sum+c[a*b][k])%mod;
}
}
printf("Case %d: %d\n",x,sum);
}
return 0;
}
六、跳蚤
题源:POJ1091
题意:意思是 给你俩数 n m
让你求有多少种组合满足:n个正整数都不大于m 并且满足从这n+1个数中选数能使得a1x1+a2x2+a3*x3+…+a(n+1)*x(n+1)=1 (这是个扩展欧几里得)
易知总共有 m^n种方法 需要减去不行的方法 不懂啊实在不懂 写出来还是不懂
这个题留坑 核心代码阶段还是不懂
核心代码段如下:
if(step>stop){//若之前已集齐
ll mm=m;
rep(i,1,stop){//让m除以所有枚举的因子
mm/=a[i];
}
temp+=quick_pow(mm,n);
return;
}
附AC代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define cl(a,b) memset(a,b,sizeof(a))
using namespace std;
//跳蚤 给你一个卡片 上面n+1个数字 先是n个数字 再是m
//其中n个数字均小于m
//题意转化后 我们要求的是 a1*x1+a2*x2+......+a(n+1)*x(n+1)=1 成立
//这个公式来源于 ax+by=gcd(a,b) 总能找到 x y(可能是负的)使得式子成立
//这里最大公约数是1 所以说就是找出所有互质的组合
//这个不好找 那么我们采用求反的方法 即求出所有非互质的组合
//所以我们需要用容斥原理 奇加偶减 搜索的方法来找出 非互质的组合
//因为可以选重复的 所以在搜索的时候要注意可以用和当前相同的
ll n,m,cnt,temp;//共cnt位
ll num[1010];//获得m的所有因子
ll a[1010];//记录当前所用的所有因子
ll quick_pow(ll a,ll b){
ll ans=1;
while(b){
if(b&1ll) ans*=a;
a*=a;
b>>=1;
}
return ans;
}
void get_num(){
cnt=0;
ll mm=m;
for(int i=2;i*i<=mm;i++){//从2开始!!!
if(mm%i==0){
num[++cnt]=i;
while(mm%i==0) mm/=i;
}
}
if(mm>1) num[++cnt]=mm;
return;
}
void dfs(int p,int step,int stop){//p 当前枚举到哪一个 step
if(step>stop){//若之前已集齐
ll mm=m;
rep(i,1,stop){//让m除以所有枚举的因子
mm/=a[i];
}//完事m还剩没被枚举到的因子相乘
//这里还是不太明白!!!拓展欧几里得!!!
//留坑!!!!
//留坑!!!!
temp+=quick_pow(mm,n);
return;
}
rep(i,p,cnt){
a[step]=num[i];//把当前位置的存上
dfs(i+1,step+1,stop);
}
}
int main() {
cin>>n>>m;
get_num();
ll res=quick_pow(m,n),ans=0;
rep(i,1,cnt){//枚举使用的因子数量
temp=0;//每次都要置0 因为每次统计temp的加减都不一样!!!
dfs(1,1,i);
if(i&1) ans+=temp;//奇数个的时候加上 偶数个的时候减去
else ans-=temp;
}
cout<<res-ans<<endl;
//system("pause");
return 0;
}