“中國東信杯”廣西大學第三屆程式設計競賽
- A A++++++++++++++++++
- B GDP Carry
- C Interpretability
- D Batch Normalization 1D
- E Antinomy與黑風海
- F Antinomy與紫水宮
- G Antinomy與伊修加德
- H Antinomy與太陽神草原
本來是當做準備校賽前的信心賽打的,結果還是有好幾道看了題解才明白.....加油吧~
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與太陽神草原
大意:
思路: