2018年全國多校演算法寒假訓練營練習比賽(第三場)
A不凡的夫夫
題目描述
夫夫有一天對一個數有多少位數感興趣,但是他又不想跟凡夫俗子一樣,所以他想知道給一個整數n,求n!的在8進位制下的位數是多少位。
輸入描述:
第一行是一個整數t(0<t<=1000000)(表示t組資料)
接下來t行,每一行有一個整數n(0<=n<=10000000)
輸出描述:
輸出n!在8進位制下的位數。示例1
輸入
3 4 2 5
輸出
2 1 3
思路: 斯特林公式。
斯特林公式(Stirling's approximation)是一條用來取n的階乘的近似值的數學公式。一般來說,當n很大的時候,n階乘的計算量十分大,所以斯特林公式十分好用,而且,即使在n很小的時候,斯特林公式的取值已經十分準確。求N!的位數:
lnN!=NlnN-N+0.5*ln(2*N*pi) !要想求有多少位,將他換成以10為底便可。利用換底公式得 lnN!/ln10=log10N!
程式碼:
#include<map> #include<vector> #include<queue> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #define maxn 100010 using namespace std; typedef long long ll; #define E 2.71828182845 #define PI 3.1415926 int main(){ ll n,t; scanf("%lld",&t); while(t--) { ll ans=1; scanf("%lld",&n); if(n>3) { ans=(log10(sqrt((long double)2.0 *PI*n))+(n*(log10((long double)n)-log10((long double)E))))/log10(8)+1; } printf("%lld\n",ans); } return 0; }
B一個小問題
題目描述
uu遇到了一個小問題,可是他不想答。你能替他解決這個問題嗎?問題:給你k對a和r是否存在一個正整數x使每隊a和r都滿足:x mod a=r,求最小正解x或無解。
輸入描述:
第一行是正整數k(k<=100000)
接下來k行,每行有倆個正整數a,r(100000>a>r>=0)
輸出描述:
在每個測試用例輸出非負整數m,佔一行。示例1
如果有多個可能的值,輸出最小的值。
如果沒有可能的值,則輸出-1。
輸入
2 8 7 11 9
輸出
31
思路:中國剩餘定理。模板題
程式碼:
#include<map> #include<vector> #include<queue> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define maxn 100010 using namespace std; typedef long long ll; void gcd(ll a,ll b,ll &d,ll &x,ll &y) { if(!b){ d=a; x=1; y=0;} else { gcd(b,a%b,d,y,x); y-=x*(a/b); } } ll c(ll n,ll a[],ll b[]) { ll m1=a[0]; ll r1=b[0]; ll flag=0; ll d; for(ll i=1;i<n;i++) { ll m2=a[i]; ll r2=b[i]; if(flag) continue; ll x,y; gcd(m1,m2,d,x,y); ll c=r2-r1; if(c%d) { flag=1; continue; } ll t=m2/d; x=(c/d*x%t+t)%t; r1=m1*x+r1; m1=m1*m2/d; } if(flag) return -1; if(n==1&&r1==0) return m1; return r1; } ll aa[550000],bb[550000]; int main() { ll k,i; cin>>k; for(i=0;i<k;i++) cin>>aa[i]>>bb[i]; cout<<c(k,aa,bb)<<endl; }
D小牛vs小客
題目描述
小牛和小客玩石子游戲,他們用n個石子圍成一圈,小牛和小客分別從其中取石子,誰先取完誰勝,每次可以從一圈中取一個或者相鄰兩個,每次都是小牛先取,請輸出勝利者的名字(小牛獲勝輸出XiaoNiu,小客獲勝輸出XiaoKe)(1 2 3 4 取走 2 13 不算相鄰)輸入描述:
輸入包括多組測試資料
每組測試資料一個n(1≤n≤1e9)
輸出描述:
每組用一行輸出勝利者的名字(小牛獲勝輸出XiaoNiu,小客獲勝輸出XiaoKe)示例1
輸入
2 3
輸出
XiaoNiu XiaoKe
思路:博弈題目,取石子(七)問題。
假設石子數等於5,如果先者先取一個,那麼後者拿走兩個,將剩下的兩個石子分成兩堆,後者贏。如果先者先取二個,那麼後者取一個使剩下的兩個石子分成兩堆,後者贏。
假設石子數等於6,如果先者先取一個,那麼後者拿走一個,將剩下的石子分成兩段,每段兩個,如果先者再拿兩個,那麼後者贏,如果先者再拿一個,那麼後者再取另一堆中的一個,這樣剩下的兩個石子被分成兩堆, 後者贏。 如果先者先取兩個,那麼後者也取兩個使剩下的兩個石子分成兩堆,後者贏。
所以當先者取走後,後者取走一個或者兩個,將剩下的石子分成對稱的兩段,以此類推,那麼如果石子數大於2後者一定贏。
程式碼:#include <stdio.h>
int main (void)
{
int n;
while (scanf("%d", &n) != EOF)
{
if(n > 2)
printf("XiaoKe\n");
else
printf("XiaoNiu\n");
}
return 0;
}
E進擊吧!階乘
題目描述
給定一個整數N(0≤N≤10000),求取N的階乘輸入描述:
多個測試資料,每個測試資料輸入一個數N
輸出描述:
每組用一行輸出N的階乘示例1
輸入
1 2 3
輸出
1 2 6
思路:Java大數階乘模板題
程式碼:
import java.math.BigInteger;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner inputScanner=new Scanner(System.in);
while(inputScanner.hasNext())
{
int n=inputScanner.nextInt();
BigInteger m;
m=BigInteger.valueOf(1);//將m定義成大數的1
for(int i=1;i<=n;i++)
{
m=m.multiply(BigInteger.valueOf(i));//大數乘法
}
System.out.println(m);
}
}
}
F小牛再戰
題目描述
共有N堆石子,已知每堆中石子的數量,兩個人輪流取石子,每次只能選擇N堆石子中的一堆取一定數量的石子(最少取一個),取過子之後,還可以將該堆石子中剩餘的石子隨意選取幾個放到其它的任意一堆或幾堆上。等哪個人無法取子時就表示此人輸掉了遊戲。注意:一堆石子沒有子之後,就不能再往此處放石子了。
假設每次都是小牛先取石子,並且遊戲雙方都絕對聰明,現在給你石子的堆數、每堆石子的數量,請判斷出小牛能否獲勝。
輸入描述:
可能有多組測試資料(測試資料組數不超過1000)
每組測試資料的第一行是一個整數,表示N(1<=N<=10)
第二行是N個整數分別表示該堆石子中石子的數量。(每堆石子數目不超過100)
當輸入的N為0時,表示輸入結束
輸出描述:
對於每組測試資料,輸出Win表示小牛可以獲勝,輸出Lose表示小牛必然會敗。示例1
輸入
3 2 1 3 2 1 1 0
輸出
Win Lose
備註:
提示:
例如:如果最開始有4堆石子,石子個數分別為3 1 4 2,而小牛想決定要先拿走第三堆石子中的兩個石子(石子堆狀態變為3 1 2 2),然後他可以使石子堆達到的狀態有以下幾種:
3 1 2 2(不再移動石子)
4 1 1 2(移動到第一堆一個)
3 2 1 2(移動到第二堆一個)
3 1 1 3(移動到第四堆一個)
5 1 0 2(全部移動到第一堆)
3 3 0 2(全部移動到第二堆)
3 1 0 4(全部移動到最後)
思路:博弈問題。取石子問題(三)
程式碼:
#include<map>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 100010
using namespace std;
int n;
int s;
int main()
{
int ans[110],i,num;
while(scanf("%d",&n)&&n)
{
memset(ans,0,sizeof(ans));
for(i=0;i<n;++i)
{
scanf("%d",&num);
ans[num]++;
}
s=0;
for(i=0;i<101;++i)
{
if(ans[i]&1)
{
s=1;
break;
}
}
if(s)
printf("Win\n");
else
printf("Lose\n");
}
return 0;
}
G大水題題目描述
給出一個數n,求1到n中,有多少個數不是2 5 11 13的倍數。輸入描述:
本題有多組輸入
每行一個數n,1<=n<=10^18.
輸出描述:
每行輸出輸出不是2 5 11 13的倍數的數共有多少。示例1
輸入
15
輸出
4
說明
1 3 7 9
思路:容斥原理:要計算幾個集合並集的大小,我們要先將所有單個集合的大小計算出來,然後減去所有兩個集合相交的部分,再加回所有三個集合相交的部分,再減去所有四個集合相交的部分,依此類推,一直計算到所有集合相交的部分。
那麼的面積就是集合A、B、C各自面積之和減去 , , 的面積,再加上的面積。
由此,我們也可以解決n個集合求並的問題。
程式碼:
#include<map>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 100010
using namespace std;
typedef long long ll;
#define PI acos(-1.0)
int main()
{
ll n;
ios::sync_with_stdio(false);
while(scanf("%lld",&n)!=EOF)
{
ll cnt;
cnt=n-(n/2)-(n/5)-(n/11)-(n/13);
cnt=cnt+(n/10)+(n/22)+(n/26)+(n/55)+(n/65)+(n/143);
cnt=cnt-(n/110)-(n/130)-(n/715)-(n/286);
cnt=cnt+(n/1430);
cout<<cnt<<endl;
}
return 0;
}