1. 程式人生 > >luogu.org 試煉場關卡2—21 簡單數學

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;
}

任務完成!正式通關!還剩一題,不寫,讓他見鬼去吧