luogu.org 試煉場關卡2—21 簡單數學
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;
}
任務完成!正式通關!還剩一題,不寫,讓他見鬼去吧