1. 程式人生 > 實用技巧 >“中國東信杯”廣西大學第三屆程式設計競賽

“中國東信杯”廣西大學第三屆程式設計競賽

目錄

本來是當做準備校賽前的信心賽打的,結果還是有好幾道看了題解才明白.....加油吧~

A A++++++++++++++++++

大意:

對於給出的n個title中,每能組成一對<national,international>,那麼競賽的等級就可以在A的基礎上增加一個+,問最後的競賽等級

思路:

取min即可

#include<iostream>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        string s;
        int num1=0,num2=0;
        for(int i=0;i<n;i++){
            cin>>s;
            if(s=="national") num1++;
            if(s=="international") num2++;
        }
        cout<<"A";
        for(int i=0;i<min(num1,num2);i++)
        {
            cout<<"+";
        }
        cout<<endl;
    }
    return 0;
}

B GDP Carry

大意:

給出n個數,Antinomy先手,需要取連續的一段和為奇的數,小西后手,需要取連續的一段和為偶的數,無法繼續則判定為輸。問在最佳策略下誰會贏

思路:

如果n個數的和為奇,那麼Antinomy先手直接勝利,如果和為偶,且存在奇數,那麼Antinomy可以先取一個奇數,使得剩下的和為奇數(偶數-奇數=奇數),此時小西由於只能選偶數,所以小西必然只能給Antinomy留下奇數,所以又轉化為了Antinomy必贏的局面,如果和為偶且不存在奇數,那麼小西必贏,因為Antinomy先手沒有數可以取。

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
long long a[N];
int main(){
        int n;
        cin>>n;
        int f=0;
        long long sum=0;
        for(int i=0;i<n;i++){
            cin>>a[i];
            sum+=a[i];
            if(a[i]%2) f++;
        }
        if(sum%2==0&&f==0) cout<<"XiJam"<<endl;
        else cout<<"Antinomy"<<endl;
    return 0;
}

C Interpretability

大意:

給出n個數字\(f_i\)代表\(2^i\)的數量,問由這些數最多能組成多少個三角形

思路:

由於都是\(2^i\),所以只可能是三個相同的數或者兩個相同的數加一個較小的數。所以從小到大掃一遍,看將\(f_i\)拿出一對一對的情況下,和前面(包括自己)單個的邊能組成三角形的數量,如果剩下了,就將其作為單個的邊記錄下來。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 4e5 + 10;
LL n, m, T, a[MAXN];

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
    LL pair = 0, single = 0, res = 0;
    LL tmp = 0, minv = 0;
    for (int i = 1; i <= n; ++i) {
        LL pair_cnt = a[i] / 2;
        LL single_cnt = a[i] % 2;
        tmp = pair_cnt;
        single += single_cnt;
        minv = min(pair_cnt, single);
        res += minv;
        LL cnt3 = (pair_cnt - minv) * 2 / 3;
        res += cnt3;
        single -= minv, single += (pair_cnt - minv) * 2 % 3;
    }
    cout << res;
    return 0;
}

D Batch Normalization 1D

大意:

模擬題

思路:

大模擬題,不想做了,piao的程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 50;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
LL powmod(LL a, LL b) { LL res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; }

int n, c, n_batch;
double eps, momentum;
double X[N][N];
double Gamma[N];
double Beta[N];
double moving_mean[N];
double moving_var[N];
double mu[N];
double var[N];
double var_tmp[N][N];
double X_hat[N][N];
double out[N][N];

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> c >> n_batch >> eps >> momentum;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= c; j++) {
			cin >> X[i][j];
		}
	}
	for (int i = 1; i <= c; i++) {
		cin >> Gamma[i];
	}
	for (int i = 1; i <= c; i++) {
		cin >> Beta[i];
	}
	for (int i = 1; i <= c; i++) {
		cin >> moving_mean[i];
	}
	for (int i = 1; i <= c; i++) {
		cin >> moving_var[i];
	}

	for (int i = 1; i <= c; i++) { //cal mu
		double sum = 0.0;
		for (int j = 1; j <= n; j++) {
			sum += X[j][i]; //order!
		}
		mu[i] = sum / n;
	}
	//cal var
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= c; j++){
			var_tmp[i][j] = pow((X[i][j] - mu[j]), 2);
		}
	}
	for (int i = 1; i <= c; i++) {
		double sum = 0.0;
		for (int j = 1; j <= n; j++) {
			sum += var_tmp[j][i]; //order!
		}
		var[i] = sum / n;
	}
	while (n_batch--) {
		for (int i = 1; i <= n; i++) { //cal X_hat
			for (int j = 1; j <= c; j++) {
				X_hat[i][j] = (X[i][j] - mu[j]) / sqrt(var[j] + eps);
			}
		}
		for (int i = 1; i <= c; i++) {
			moving_mean[i] = moving_mean[i] * momentum + (1.0 - momentum) * mu[i];
		}
		for (int i = 1; i <= c; i++)  {
			moving_var[i] = moving_var[i] * momentum + (1.0 - momentum) * var[i];
		}
		for (int i = 1; i <= n; i++) { //cal out
			for (int j = 1; j <= c; j++) {
				out[i][j] = X_hat[i][j] * Gamma[j] + Beta[j];
			}
		}
	}
	//last
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= c; j++) {
			X_hat[i][j] = (X[i][j] - moving_mean[j]) / sqrt(moving_var[j] + eps);
		}
	}
	for (int i = 1; i <= n; i++) { //cal out
		for (int j = 1; j <= c; j++) {
			out[i][j] = X_hat[i][j] * Gamma[j] + Beta[j];
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= c; j++) {
			cout << out[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
}

E Antinomy與黑風海

大意:

有向圖,從點1出發,每次去其他的點做任務,做完回到1點後才能去下一個點做任務,問做完全部任務需要走多遠

思路:

很容易看出來前去做任務就是求點1到其他所有點的距離即可,那麼怎麼求回來的距離呢?直接把每條邊反向,重新建一遍圖即可

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<LL, int> PII;
int const MAXN = 1e6 + 10, MAXM = 2 * MAXN;
int n, m, T, h[MAXN], e[MAXM], ne[MAXM], idx, w[MAXM], st[MAXN];
LL dis[MAXN];
struct Edge {
    int u, v, c;
}edge[MAXM];

void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void dijkstra() {
    memset(dis, 0x3f, sizeof dis);  // 初始化距離為無窮
    priority_queue<PII, vector<PII>, greater<PII> > q;  // 定義一個按照距離從小到大排序的優先佇列,第一維:距離,第二維:點
    dis[1] = 0;  // 一開始源點距離為0
    q.push({0, 1});  // 把源點資訊放入佇列
    while (q.size()) {  // 每個點只出入佇列一次
        auto t = q.top();
        q.pop();
        
        LL distance = t.first;
        int ver = t.second;  // 最小距離和相對應的點
        if (st[ver]) continue;  // 這個操作保證每個點只出入隊一次,因為佇列裡面可能會出現{dis1[3], 3}, {dis2[3], 3}的情況,這樣保證dis1[3]<dis2[3]時,3號點只進出入隊一次
        st[ver] = 1;  // 標記,因為dijkstra的貪心策略保證每個點只需要進出隊一次
        
        for (int i = h[ver]; ~i; i = ne[i]) {  // 遍歷ver的鄰接點
            int j = e[i];
            if (dis[j] > distance + (LL)w[i]) {
                dis[j] = distance + (LL)w[i];
                q.push({dis[j], j});  // 這裡不需要判斷st,因為一旦更新發現更小必須放入佇列
            }
        }
    }
}

int main() {
    memset(h, -1, sizeof h);
    idx = 0;
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        edge[i] = {a, b, c};
        add(a , b, c);
    }
    dijkstra();
    LL res = 0;
    for (int i = 1; i <= n; ++i) {
        h[i] = -1;
        st[i] = 0;
        if (i != 1) res += dis[i];
    }
    idx = 0;
    for (int i = 1; i <= m; ++i) {
        int a = edge[i].u, b = edge[i].v, c = edge[i].c;
        add(b, a, c);
    }
    dijkstra();
    for (int i = 2; i <= n; ++i) {
        res += dis[i];
    }
    cout << res;
    return 0;
}

F Antinomy與紫水宮

大意:

給出一個樹,樹上每對距離小於3的節點顏色都不能相同,現在有m種顏色,問有多少種塗色方法

思路:

樹形dp,首先固定每個節點與其父節點的顏色,那麼子節點的顏色數利用組合數可以算出來\(A_{m=2}^{子節點數量}\),然後向父節點傳遞即可。注意根節點處因為沒有父節點所以要單獨寫

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5, mod = 1e9 + 7;
typedef long long LL;
int n, m;
vector<int> mp[N];
int fact[N], infact[N];

LL qmi(LL a, LL k, LL p) {
    LL res = 1;
    while (k) {
        if (k & 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}

// 預處理
void init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; ++i) {
        fact[i] = (LL)fact[i - 1] * i % mod;
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
}

int Cnm(int a, int b) {
    return (LL)fact[a] * infact[b] % mod * infact[a - b] % mod;
}

int res = 1;
void dfs(int now, int fa) {
    res = (LL)res * Cnm(m - 2, mp[now].size() - 1) % mod *
          fact[mp[now].size() - 1] % mod;  //求Anm;
    for (int i = 0; i < mp[now].size(); i++) {
        int ne = mp[now][i];
        if (ne == fa) continue;
        dfs(ne, now);
    }
}
int main() {
    cin >> n >> m;
    init();
    for (int i = 0; i < n - 1; i++) {
        int x, y;
        cin >> x >> y;
        mp[x].push_back(y);
        mp[y].push_back(x);
    }
    res = (LL)res * Cnm(m, mp[1].size() + 1) % mod * fact[mp[1].size() + 1] % mod;
    for (int i = 0; i < mp[1].size(); i++) {
        dfs(mp[1][i], 1);
    }
    cout << res << endl;
    return 0;
}

G Antinomy與伊修加德

大意:

Antinomy想買一些盤子來裝拉拉肥,這樣可以打包賣出去,每個拉拉肥不能拆開賣。

每個拉拉肥都有售價,第i個拉拉肥賣出去後的售價是\(p_i\)金幣。

每個盤子也需要花錢買,第iii個盤子需要花\(c_i\)金幣,能裝\(b_i\)個拉拉肥。

每個拉拉肥可以賣也可以不賣,但賣就必須要放在一個盤子裡。Antinomy的利潤就是賣出去的拉拉肥售價總和,減去盤子的花費。

思路:

類似01揹包的問題,每個盤子取或不取,價值就是拉拉肥的收益-盤子價格,體積就是拉拉肥的個數,注意要先將拉拉肥按照價值排序,因為拉拉肥的體積相同,那麼肯定是優先賣價值高的

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n, m, p[N], b[N], c[N], pre[N],dp[N];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
    }
    sort(p + 1, p + 1 + n, greater<int>());
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + p[i];
    }
    for (int i = 0; i < m; i++) {
        cin >> b[i] >> c[i];
    }
    int res = 0;
    for (int i = 0; i < m;i++){
        for (int j = n; j >= 0;j--){
            if(j>=b[i])
                dp[j] = max(dp[j], dp[j - b[i]] + pre[j] - pre[j - b[i]] - c[i]);
            else
                dp[j] = max(dp[j], pre[j] - c[i]);
            res = max(res, dp[j]);
        }
    }
    cout << res << endl;
    return 0;
}

H Antinomy與太陽神草原

大意:

思路: