1. 程式人生 > >"位元組跳動杯"2018中國大學生程式設計競賽-女生專場(ing)

"位元組跳動杯"2018中國大學生程式設計競賽-女生專場(ing)

1002. 口算訓練

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6287

Problem Description

小Q非常喜歡數學,但是他的口算能力非常弱。因此他找到了小T,給了小T一個長度為n的正整數序列a1,a2,...,an,要求小T丟擲m個問題以訓練他的口算能力。
每個問題給出三個正整數l,r,d,小Q需要通過口算快速判斷al×al+1×...×ar−1×ar是不是d的倍數。
小Q迅速地回答了出來,但是小T並不知道正確答案是什麼,請寫一個程式幫助小T計算這些問題的正確答案。

Input

第一行包含一個正整數T(1≤T≤10),表示測試資料的組數。

每組資料第一行包含兩個正整數n,m(1≤n,m≤100000),分別表示序列長度以及問題個數。

第二行包含n個正整數a1,a2,...,an(1≤ai≤100000),表示序列中的每個數。

接下來m行,每行三個正整數l,r,d(1≤l≤r≤n,1≤d≤100000),表示每個問題。

Output

對於每個問題輸出一行,若是倍數,輸出Yes,否則輸出No。

 

Solution:

將輸入的每個數分解質因數並記錄,對d分解質因數並判斷每個質因數在陣列中是否有足夠的因數。

[l, r] 中每個數相乘的結果是d的倍數,只需要 [l, r] 之間每個數的因數選擇一些乘起來是 d 或 d 的倍數即可。但是暴力的儲存查詢會超時,所以可以通過用 vector 存因子 x 出現的所有座標 i,然後再利用二分查詢當前範圍內由多少個因數。

注意,分解的時候要注意當前數是素數的情況。

 

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <cstdlib>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MaxN = 1e5 + 5;

vector<int> G[MaxN];

int query(int l, int r, int x) 
{
    return upper_bound(G[x].begin(), G[x].end(), r) - lower_bound(G[x].begin(), G[x].end(), l);
}

bool check(int l, int r, int d) 
{
    for(int i = 2; i * i <= d; i++) {
        if(d % i == 0) { //對d分解質因數
            int cnt = 0;
            while(d % i == 0) {
                cnt++;
                d /= i;
            }
            if(query(l, r, i) < cnt) return 0;
        }
    }
    if(d > 1 && query(l, r, d) < 1) return 0; //如果當前數是素數
    return 1;
}

int main () 
{
    int t; scanf("%d", &t);
    while(t--) {
        for(int i = 1; i <= MaxN; i++) G[i].clear();
        int n, m;
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= n; i++) {
            int x; scanf("%d", &x);
            for(int j = 2; j * j <= x; j++) { //對陣列中的數分解質因數
                while(x % j == 0) {
                    x /= j;
                    G[j].push_back(i); //對於每次當前的質因數都儲存座標i
                }
            }
            if(x > 1) G[x].push_back(i); //當前數是素數
        }
        while(m--) {
            int l, r, d;
            scanf("%d %d %d", &l, &r, &d);
            if(check(l, r, d)) printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}

 

1003. 缺失的資料範圍

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6288

Problem Description

著名出題人小Q出過非常多的題目,在這個漫長的過程中他發現,確定題目的資料範圍是非常痛苦的一件事。
每當思考完一道題目的時間效率,小Q就需要結合時限以及評測機配置來設定合理的資料範圍。
因為確定資料範圍是一件痛苦的事,小Q出了非常多的題目之後,都沒有它們設定資料範圍。對於一道題目,小Q會告訴你他的演算法的時間複雜度為O(nalogbn),且蘊含在這個大O記號下的常數為1。同時,小Q還會告訴你評測機在規定時限內可以執行k條指令。小Q認為只要na(⌈log2n⌉)b不超過k,那麼就是合理的資料範圍。其中,⌈x⌉表示最小的不小於x的正整數,即x上取整。
自然,小Q希望題目的資料範圍n越大越好,他希望你寫一個程式幫助他設定最大的資料範圍。

 Input

第一行包含一個正整數T(1≤T≤1000),表示測試資料的組數。
每組資料包含一行三個正整數a,b,k(1≤a,b≤10,106≤k≤1018),分別描述時間複雜度以及允許的指令數。

 Output

對於每組資料,輸出一行一個正整數n,即最大可能的n。

 

Description:

給出a、b、k,對於公式 n^{a}(\left \lceil log_{2}^{n} \right \rceil)^{b} \leq k 找出最大的n。

 

Range:

1 ≤ a, b ≤ 10

1e6 ≤ k ≤ 1e18

 

Solution:

二分查詢可行解。

把公式分為 n^{a} 和 (\left \lceil log_{2}^{n} \right \rceil)^{b} 兩部分分別進行判斷。

需要注意的兩點是:

  • 乘法可能溢位,需要將乘法轉換為除法 
  • 求 ⌈log2 n⌉ 時不能使用實數計算,會帶來誤差,應當使用整數計算。

 

Code:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define fi first
#define se second
#define mst(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e9 + 7;
const int MaxN = 1e5 + 5;

int a, b;
LL k;

LL log2(LL mid)
{
	for(int i = 0; i <= 64; i++) {
		if(pow(2LL, i) >= mid) return i;
	}
}

bool check(LL mid) 
{
	LL pre = 1LL;
	for(int i = 1; i <= a; i++) { //判斷前面部分
		if(pre <= k / mid) pre *= mid;  //防止乘法溢位
		else return false;  //當前的結果大於k說明不合法
	}
	LL base = log2(mid);
	LL suf = 1LL;
	if(suf == 0) return true;  //否則會在下面的除法中出現RE
	for(int i = 1; i <= b; i++) {  //判斷後面部分
		if(suf <= k / mid) suf *= base;
		else return false;
	}
	if(pre <= k / suf) return true;  //將兩部分合起來判斷一次
	return false;
}

int main()
{
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    int t; scanf("%d", &t);
    while(t--) {
    	scanf("%d %d %lld", &a, &b, &k);
    	LL l = 0LL, r = 1e18, ans = 0LL;
    	while(l <= r) {
			LL mid = (l + r) / 2;
			if(check(mid)) l = mid + 1, ans = mid;
			else r = mid - 1;
		}
		cout << ans << endl;
	}
    
    return 0;
}

 

1004. 尋寶遊戲

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6289

Problem Description

小Q最近迷上了一款尋寶遊戲,這款遊戲中每局都會生成一個n×m的網格地圖,從上往下依次編號為第1行到第n行,從左往右依次編號為第1列到第m列。每個格子上都有不同數量的金幣,第i行第j列的格子上的金幣數量為ai,j。
小Q一開始位於(1,1),每次他可以往右或者往下走,每當他經過某個格子時,他就可以拿走這個格子上的所有金幣。小Q不能走出這個地圖,當小Q不能再行動時,遊戲結束。顯然當且僅當小Q位於(n,m)時,遊戲才會結束。
一輪遊戲的得分為這一輪中收集到的金幣總量,而在遊戲開始前,因為小Q是超級VIP使用者,所以他有k次機會交換某兩個格子中的金幣數。這k次機會不一定要用完,請寫一個程式幫助小Q在一輪內拿到儘可能多的金幣。

Input

第一行包含一個正整數T(1≤T≤10),表示測試資料的組數。
每組資料第一行包含三個整數n,m,k(2≤n,m≤50,0≤k≤20),分別表示地圖的長寬以及交換的次數。
接下來n行,每行m個整數ai,j(0≤ai,j≤106),依次表示每個格子中金幣的數量。

 Output

對於每組資料,輸出一行一個整數,即能收集到的金幣數量的最大可能值。

 

Description:

對於 n*m 的矩陣每個點都有一個權值。從 (1, 1) 開始每次只能向下或向右走,求到達 (n, m) 時可以獲得的最大值,其中你有k次交換機會交換任意兩點上的權值。

 

Solution:

假設已經選定了一條路線,那麼不考慮具體交換方案,考慮選定一個 t(t ≤ k),經過的格子裡要有 t 個格子的權值不計入得分,而不經過的格子裡要有 t 個格子的權值計入總分。那麼每一種交換方案都可以對應這樣一個轉化。
設 f_{i,j,x,y}  表示從 (1, 1)  出發來到 (i, j),考慮完前 i − 1 行所有格子以及第 i 行前 j 個格子時,有 x 個經過的格子不計分,y 個不經過的格子計分的情況下,總分的最大值是多少。那麼有兩種狀態轉移:

  • 往右走一格,直接轉移到f_{i, j+1, {x}', {y}'};
  • 往下走一格,轉移到f_{i+1, j, {x}', {y}'},需要列舉這一行有多少個不經過的格子計分。顯然這些

格子一定按照權值從大到小貪心選擇。
最終答案即為 max(f_{n, m,t,t}) (0 ≤ t ≤ k)

時間複雜度O(n^{2}k^{2})。