p1374 倒水 进制 || 枚举、暴力
题目
https://www.luogu.org/problemnew/show/P1582
题解
一句话题意:将n加x,使得他们的和在二进制下有<=k个1,且x最小。
理解了题意,其实就很简单了:
1.码出求n(包括n+x)在二进制下有几个1的函数;(如果不会,网上学习。)
2.使用一下lowbit(就是树状数组的那个lowbit);
然后,你就会发现这道题就A了!!!!!
不懂就继续往下看!
引用一下 https://www.luogu.org/blog/user36297/solution-p1582
这题第一眼看到就知道和二进制有关。
合并前 | 二进制 | 合并后 |
---|---|---|
1个瓶子 | 1 | 1个瓶子 |
2个瓶子 | 10 | 1个瓶子 |
3个瓶子 | 11 | 2个瓶子 |
4个瓶子 | 100 | 1个瓶子 |
5个瓶子 | 101 | 2个瓶子 |
6个瓶子 | 110 | 2个瓶子 |
7个瓶子 | 111 | 3个瓶子 |
8个瓶子 | 1000 | 1个瓶子 |
… | … | … |
根据上列式子,我们知道n个有水的瓶子,最后合并成的瓶子个数,就是这个数转成二进制1的个数。
我们知道,二进制有一个很好的取各位上1的个数的方法。
首先我们来认识一个式子:
i&-i(C++),i and -i(pascal)
这个式子返回的值就是从后往前数,到第一个1出现为止的数(二进制下)。
来列个表:
i | i(2) | i&-i | i&-i(2) |
---|---|---|---|
1 | 1 | 1 | 1 |
2 | 10 | 2 | 10 |
3 | 11 | 1 | 1 |
4 | 100 | 4 | 100 |
5 | 101 | 1 | 1 |
6 | 110 | 2 | 10 |
7 | 111 | 1 | 1 |
8 | 1000 | 8 | 1000 |
然后,如果要取x中1的个数(二进制下),那么就写这样一段代码:
int work(int x)
{
int num=0;
for (;x;x-=x&-x)
++num;
return num;
}
num就是1的个数。
然后,我们解决添加的问题,一个个添加太慢了,我们应该一次添加一堆,这一堆添上去,刚好能让总共的水杯个数减少(或不变)。
我们有个贪心的想法,从少的开始添,否则就浪费了,明明可以要黄金,你却偏偏喜欢白银(我也无能为力)。
那么就用到上面的这个式子了,在最后一位1再添上个1,那么就会进位,水杯的个数只会减少(或不变),而不会增多。
最后个数小于等于k时就可以停了。
代码
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,num=0;
char ch=getchar();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
return num*f;
}
int lowbit(int x)
{
return x & -x;
}
int count_one_bits(unsigned int n)
{
int count = 0;
for (int i=1;i;i<<=1)//判断每一位的值是否为1
{
if ((n & 1)==1)
++count;
//右移准备判断下一位的值
n>>=1;
}
return count;
}
int main()
{
int n=read(),k=read(),ans=0;
while (count_one_bits(n)>k)
ans+=lowbit(n),n+=lowbit(n);
printf("%d\n",ans);
return 0;
}
p2158 [sdoi2008]仪仗队 素数判断,质数,筛法 || 最大公约数,gcd
题目
https://www.luogu.org/problemnew/show/P2158
题解
《算法竞赛进阶指南》。。。。。。。。T_T
代码
#include<bits/stdc++.h>
#define up(i,a,b) for (register int i=a;i<=b;++i)
#define down(i,a,b) for (register int i=a;i>=b;--i)
using namespace std;
const int maxn=4e4+10;
inline int read()
{
int f=1,num=0;
char ch=getchar();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
return num*f;
}
int ans;
int v[maxn],prime[maxn],phi[maxn];
void eular(int n)
{
memset(v,0,sizeof(v));//最小质因子
int m=0;//质数数量
up(i,2,n)
{
if (!v[i])//i是质数
{
v[i]=i,prime[++m]=i;
phi[i]=i-1;
}
up(j,1,m)//给当前的数i乘上一个质因子
{
if (prime[j]>v[i] || prime[j]>n/i) break;
//i有比prime[j]更小的质因子,或者超出n的范围
v[i*prime[j]]=prime[j];//prime[j]是合数i*prime[j]的最小质因子
phi[i*prime[j]]=phi[i]*(i%prime[j] ? prime[j]-1 : prime[j]);
}
}
}
int main()
{
int n=read();
if (n==1)
{
puts("0");
exit(0);
}
eular(n);
up(i,2,n-1)
ans+=phi[i];
printf("%d\n",ans*2+3);
return 0;
}
P1372 又是毕业季I 最大公约数,gcd
题目
https://www.luogu.org/problemnew/show/P1372
代码
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,num=0;
char ch=getchar();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
return num*f;
}
int main()
{
int n=read(),k=read();
printf("%d\n",n/k);
return 0;
}
总之,我想吐血。。。。。。。
P1865 A % B Problem 前缀和||素数判断,质数,筛法
题目
https://www.luogu.org/problemnew/show/P1865
代码
#include<bits/stdc++.h>
#define up(i,a,b) for (register int i=a;i<=b;++i)
#define down(i,a,b) for (register int i=a;i>=b;--i)
using namespace std;
const int maxn=1e6+10;
inline int read()
{
int f=1,num=0;
char ch=getchar();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
return num*f;
}
int v[maxn],f[maxn];
void primes(int n)
{
v[1]=1;
f[1]=0;
up(i,2,n)
{
if (!v[i])
{
f[i]=f[i-1]+1;
up(j,i,n/i)
v[i*j]=1;
}
else f[i]=f[i-1];
}
}
int main()
{
int n=read(),m=read();
primes(maxn);
up(i,1,n)
{
int l=read(),r=read();
if (l<1 || r>m)
puts("Crossing the line");
else
{
int ans=f[r]-f[l-1];
printf("%d\n",ans);
}
}
return 0;
}
任务完成!正式通关!还剩一题,不写,让他见鬼去吧