西北大學2018第四屆超萌新生賽題解

 比賽網址:https://ac.nowcoder.com/acm/contest/321#questionios

你們好,我是此次比賽負責人NE,c++

本次比賽志在"零板子也能AK",全程面向新生,不防AK算法


A 容斥定理,顯然能被A若是暴力的減去$n/A,n/B,n/C$會衝突,例如2和3,會重複減去6,12等等,因而用容斥定理便可解決問題,數組

      $S=x-(x/a+x/b+x/c-x/ab-x/ac-x/cb+x/abc),\ \ x=1000,000,000$函數

cin>>casn;
while(casn--){
	ll a,b,c;
	ll all=1000000000;
	ll ans=0;
	cin>>a>>b>>c;
	ans=all/a+all/b+all/c-all/(a*b)-all/(a*c)-all/(b*c)+all/(a*b*c);
	cout<<all-ans<<endl;
}

  *本來這個題是ABC三個數都必定不是素數,被NE削弱了優化


B 計數,結構體排序,新生要學會使用sort函數和自定義cmp函數ui

若是會使用pair,就更方便了spa

int n,m,k;
cin>>n>>m;
vector<pair<int,int> > ans(m);
for(int i=1;i<=m;i++) ans[i-1].second=i;
while(n--){
	cin>>k;
	while(k--){
		int a;
		cin>>a;
		ans[a-1].first++;
	}
}
sort(ans.begin(),ans.end());
for(auto i:ans) cout<<i.second<<' '<<i.first<<endl;
return 0;

 C找規律便可,不須要圖論知識,注意會爆intcode

int casn;
long long n,m;
int main() {
	  cin>>casn;
		while(casn--){
			cin>>n>>m;
			cout<<n*(n-1)/2-m<<endl;
		}
	return 0;
}

D一眼感受多是$bfs$,可是顯然能夠反向考慮,當前是$N$,如何最快的變成1?blog

顯然,若是當前是奇數,沒法整除二,只能減一,若是是偶數,除二的收益顯然大於減一.

因而不斷除二取餘便可

while(cin>>n){
    int ans=0;
    while(n!=1){
        if(n%2) ans++;
        ans++;
        n/=2;
    }
    cout<<ans<<endl;
}  

 E照題意模擬便可,注意純正九蓮寶燈對手牌有要求

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20;
struct Tile{
	char type;
	int id;
	bool operator == (const Tile& T) const {
		if (type == T.type && id == T.id) return 1;
		else return 0;
	}
}hand[maxn];
int getPower(char c) {
	if (c == 'm') return 1;
	else if (c == 'p') return 2;
	else if (c == 's') return 3;
	else return 4;
}
int main () {
	int T; cin >> T;
	while (T--) {
		for (int i = 0; i < 14; i++) {
			string buf; cin >> buf;
			hand[i].type = buf[1];
			hand[i].id = buf[0] - '0';
		}
		sort(hand, hand + 13, [](Tile& a, Tile& b) {
			if (getPower(a.type) < getPower(b.type)) return 1;
			else if (getPower(a.type) > getPower(b.type)) return 0;
			else if (a.id < b.id) return 1;
			else return 0;
		});
		bool ans = 0;
		//國士無雙十三面
		if (hand[0] == (Tile){'m', 1} && hand[1] == (Tile){'m', 9} && hand[2] == (Tile){'p', 1} &&
			hand[3] == (Tile){'p', 9} && hand[4] == (Tile){'s', 1} && hand[5] == (Tile){'s', 9} &&
			hand[6] == (Tile){'z', 1} && hand[7] == (Tile){'z', 2} && hand[8] == (Tile){'z', 3} &&
			hand[9] == (Tile){'z', 4} && hand[10] == (Tile){'z', 5} && hand[11] == (Tile){'z', 6} &&
			hand[12] == (Tile){'z', 7} &&
			(hand[13] == hand[0] || hand[13] == hand[1] || hand[13] == hand[2] ||
			 hand[13] == hand[3] || hand[13] == hand[4] || hand[13] == hand[5] ||
			 hand[13] == hand[6] || hand[13] == hand[7] || hand[13] == hand[8] ||
			 hand[13] == hand[9] || hand[13] == hand[10] || hand[13] == hand[11] ||
			 hand[13] == hand[12])) ans = 1;
		//純正九蓮寶燈
		//判斷花色是否一致
		bool jiulianFlag = 1;
		for (int i = 1; i < 14; i++) {
			if (hand[i].type == 'z' || hand[i].type != hand[i - 1].type) {
				jiulianFlag = 0;
				break;
			}
		}
		if (jiulianFlag) {
			if (hand[0].id == 1 && hand[1].id == 1 && hand[2].id == 1 &&
				hand[3].id == 2 && hand[4].id == 3 && hand[5].id == 4 &&
				hand[6].id == 5 && hand[7].id == 6 && hand[8].id == 7 &&
				hand[9].id == 8 && hand[10].id == 9 && hand[11].id == 9 &&
				hand[12].id == 9) ans = 1;
		}
		//大四喜
		int z1cnt = 0;
		int z2cnt = 0;
		int z3cnt = 0;
		int z4cnt = 0;
		int dasixiJiang1 = 15;
		int dasixiJiang2 = 15;
		for (int i = 0; i < 14; i++) {
			if (hand[i] == (Tile){'z', 1}) z1cnt++;
			else if (hand[i] == (Tile){'z', 2}) z2cnt++;
			else if (hand[i] == (Tile){'z', 3}) z3cnt++;
			else if (hand[i] == (Tile){'z', 4}) z4cnt++;
			else if (dasixiJiang1 == 15) dasixiJiang1 = i;
			else dasixiJiang2 = i;
		}
		if (z1cnt == 3 && z2cnt == 3 && z3cnt == 3 && z4cnt == 3 && hand[dasixiJiang1] == hand[dasixiJiang2]) ans = 1;
		cout << (ans ? "YES" : "NO") << endl;
	}
}

F利用類似三角形模擬便可

也能夠用圓外點到圓割線與切線的關係等價計算,此方法全部計算均可以在整數下進行,無精度偏差

從圓外一點引圓的兩條割線,這一點到每條割線與圓交點的距離的積相等,(切線也知足)

typedef long long ll;
int main() {
	int T;
	cin>>T;
	while(T--) {
		ll fx,fy,rx,ry,r;
		int n;
		scanf("%lld %lld %lld %lld %d",&fx,&fy,&rx,&r,&n);
		ry=fy;
		ll len=(fx-rx)*(fx-rx)-r*r;
		ll cnt=0;
		ll all=0;
		for(int i=0; i<n; i++) {
			ll y0,data;
			scanf("%lld %lld",&y0,&data);
			all+=data;
			ll a=fy-y0;
			ll b=-fx;
			ll c=y0*fx;
			ll d=(a*rx+b*ry+c)*(a*rx+b*ry+c);
			ll R=r*r*(a*a+b*b);
			if(d<=R) {
				all=all-min(data,len);
			}
		}
		printf("%lld\n",all);
	}
}

  *本來不保證高度相同,存在相切的狀況,且存在直線相交但射線不相交的狀況,且輸出爲誤導性的"四捨五入到整數位",被NE削弱了


 G顯然賣萌值的前綴和在正整數域內爲一個單調遞增級數,能夠近似理解爲$N^3$,函數,估算可知$10^18$所須要的賣萌次數不會超過$10^6$數量級(實際爲140000左右)

所以,預處理出前綴和,保證在數組中,對於每次詢問,進行二分查找便可,複雜度約爲$O(10^6+Tlog(10^6))$

記憶力好的直接用公式二分也行

ios::sync_with_stdio(false);
  cin.tie(0);
  cout << fixed << setprecision(15);
  const int N = 15e5;
  vector<long long> pre(N);
  int n;
  for(int i = 1; i < N; i++) {
    pre[i] = 1LL * i * i;
    pre[i] += pre[i - 1];
    n = i;
    if(pre[i] > (long long)1e18) break;
  }
  int T;
  cin >> T;
  while (T--) {
    long long k;
    cin >> k;
    auto it = lower_bound(pre.begin(), pre.begin() + n, k);
    if(*it > k) it--;
    long long ans = *it;
    cout << ans << '\n';
  }

 H根據行列的關係能夠推公式

$A = (x_{min}+x_{max})(x_{max}-x_{min}+1)/2$

$B=(x_{max}-x_{min}+1)$

$C=(y_{min}+y_{max})(y_{max}-y_{min}+1)/2$

$D=(y_{max}*(y_{{max}}+1)*(2y_{max}+1)-(y_{min}-1)(y_{min})(2y_{min}-1))/6$

$Ans = A * C + B * (C-D)$

或者

能夠先轉化爲原來矩陣的位置,直接算,更方便

設$a = xmax-xmin$,$b=ymax-ymin$。能夠把每一個元素對應到原矩陣上的位置,就會發現是求原矩陣中一個平行四邊行中全部元素的和。平行四邊形的頂點分別是$(xmin,ymin),(xmax,ymin),(xmin-b,ymin+b),(xmax-b,y+b)$。

對於第$ymin+i(i<=b)$列的全部所須要的元素,它們的和爲$(ymin+i)*(xmin+xmax-2*i)*(xmax-xmin+1)/2$

而後只須要對上式的i從0到b求和便可。

最後的式子是$(a+1)*b*(ymax*(xmax+xmin)+(xmin+xmax-2*ymin)*(b+1)/2-(b+1)*(2*b+1))/2$

同時還要注意除法須要用到分母對於1000000007的模逆元

int T;
cin>>T;
ll inv = powmod(6,mod-2);
while(T--) {
	ll u,v,x,y; //u:xmin,v:xmax,x:ymin,y:ymax
	cin>>u>>x>>v>>y;
	ll a = (u+v) * (v-u+1) / 2 % mod;
	ll b = (v-u+1)%mod;
	ll c = (x+y)*(y-x+1)/2%mod;
	ll d = y*(y+1)%mod*(2*y+1)%mod - (((x-1)*x%mod*(2*x-1)%mod)%mod+mod)%mod;
	d = d * inv % mod;
	ll ans = a * c % mod+ b * ((c - d) % mod + mod)% mod;
	ans %= mod;
	cout<<ans<<endl;
}  

I 顯然$O(n^2)$的暴力會超時,因而考慮優化

創建兩個標記l,r,對於區間[l,r]維護其中的種類數, 若是已經知足,就保存一次答案,並嘗試刪除$l$處的元素,並使$l=l+1$,直到再也不知足答案爲止,當前不知足答案時,嘗試將$r+1$處的元素加入,使得$r=r+1$

顯然,每一個數組位置只會被遍歷至多2次,複雜度$O(n)$

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6+10;
int a[maxn], num[maxn];
int main() {
    int t;
    scanf("%d", &t);
    while(t--) {
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 0; i < n; i++) {
            scanf("%d", a+i);
            if(a[i] > k)  a[i] = 0;
        }
        memset(num, 0, sizeof num);
        int l = 0, r = 0, ans = 1e7, len = 0;
        num[0] = 1e7;
        for(r = 0; r < n; r++) {
            if(a[r] != 0 && num[a[r]] == 0) len++;
            num[a[r]]++;
            while(len >= k) {
                if(a[l] != 0 && num[a[l]] == 1) {
                    len--;
                    ans = min(ans, r-l+1);
                }
                num[a[l]]--;
                l++;
                while(l < n && a[l] == 0)  l++;
            }
        }
        if(ans == 1e7)  printf("-1\n");
        else printf("%d\n", ans);
    }
    return 0;
}

J 首先,一個集合內出現屢次的元素沒有意義,因此要去重,去重後的數組最大爲100,

而後若是暴力每一個集合選一個,複雜度顯然爆炸

可是能夠發現等價的枚舉方式,即先結合前2個數組,將結果再結合第3個數組...以此類推,每次結合一個新數組後,值域會增長,最後的總複雜度就是$O((N*(N+1)/2)*3e2*3e2)$

能夠理解爲簡單的分組揹包

存在更優秀的作法,例如不斷兩兩結合(本來std要用這個,可是和揹包拉不開數量級,索性不卡了..

#include <bits/stdc++.h>
using namespace std;
 
int main() {
#ifdef LOCAL_DEFINE
    freopen("data.in", "rt", stdin);
    // freopen("data.out", "w", stdout);
    auto _start = chrono::high_resolution_clock::now();
#endif
 
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(15);
    int n;
    cin >> n;
    vector<vector<bool>> a(n);
    for (int i = 0; i < n; i++) {
        int k;
        cin >> k;
        a[i].resize(301);
        for (int j = 0; j < k; j++) {
            int x;
            cin >> x;
            a[i][x] = true;
        }
    }
    vector<vector<bool>> dp(n, vector<bool>(30001));
    for (int i = 0; i <= 300; i++) {
        if (a[0][i]) {
            dp[0][i] = true;
        }
    }
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= 300; j++) {
            for (int k = 1; k <= i * 300; k++) {
                if (dp[i - 1][k] && a[i][j]) {
                    dp[i][k + j] = true;
                }
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= 30000; i++) {
        ans += dp[n - 1][i];
    }
    cout << ans << '\n';
 
#ifdef LOCAL_DEFINE
    auto _end = chrono::high_resolution_clock::now();
    cerr << "elapsed time: " << chrono::duration<double, milli>(_end - _start).count() << " ms\n";
#endif
    return 0;
}

K排序以後,從編號[l,r]裏面選出來的集合
若是包含a[l] a[r],那麼他們都是a[l]*a[r],該值的貢獻次數就是2^(r-l-1)
把答案表達式進行簡單的因子提取,會發現對於每一個右端點
(a[0]*2^(r-1)+a[1]*2^(r-2)+...a[r-1]*2(0) ) *a(r)
發現能夠用相似秦九昭算法的方法來從進行O(n)計算

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 1e9+7;
const int maxn = 1e5+10;
ll a[maxn];
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)   scanf("%lld", a+i);
    sort(a, a+n);
    ll last = 0, ans = 0;
    for(int i = n-2; i >= 0; i--) {
        last = (last*2%mod+a[i+1])%mod;
        ll tmp = a[i]*last%mod;
        ans += tmp;
        ans %= mod;
    }
    printf("%lld\n", ans);
    return 0;
}


 L因爲購買策略是"當前錢夠就買",因此並非帶的錢越多能買的罐子數量越多(無單調性)

好比最多能帶5元,商店有3個罐子,價格分別爲5 1 1.若是帶4元能買2個罐子,而帶5元只夠買一個.因此並不能用二分.注意到罐子的數量*每一個罐子的最大價格最多隻有3e5,則可使用枚舉法,枚舉帶的錢數,設罐子價格總和爲sum,則只須要從1元枚舉到min(sum+1,n).

對於每次枚舉,判斷是否能買m個罐子便可.

 注意特殊樣例

100 1 1

0

答案應爲 1

#include <bits/stdc++.h>
using namespace std;
int jar[305],n,m,k;
bool check(int money){
	int buy=0;
	for(int i=1;i<=k;i++){
		if(money>=jar[i]){
			money-=jar[i];
			buy++;
		}
		if(money==0||buy>=m)
			break;
	}
	return buy>=m;
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++)
			cin>>jar[i];
	int len=min(k*1001,n);
	for(int i=1;i<=len;i++)
		if(check(i))
			return cout<<i<<endl,0;
	cout<<"poor chicken tail wine!"<<endl;
}

 M簡單思考發現,必定能夠刪除到0,接下來考慮全部數字二進制轉化,把全部數字或起來,二進制下的1的個數即爲答案

*本來題面寫的是,所有清空的最少操做次數,NE以爲這個題太水,今早靈基一動,改爲了儘可能少

ios::sync_with_stdio(false);
  int m;
  cin >> m;
  while(m--) {
    int n;
    cin >> n;
    vector<int> a(n);
    int sum = 0;
    for(int i = 0; i < n; i++) {
      cin >> a[i];
      sum |= a[i];
    }
    int ans = __builtin_popcount(sum);
    cout << ans << '\n';
  }



 

相關文章
相關標籤/搜索