第九屆福建省大學生程式設計競賽部分題解
阿新 • • 發佈:2018-12-29
比賽就這樣過去了,A2題水了個銅獎第三回來= =感覺能A3拿個銀獎的
目錄
A:Uint47 calculator
模擬47位計算,當時我沒想起來快速乘,結果用java大數,報了一個我見都沒見過的re,後來好久以後,問了工作人員,然後才重判A了,其實真的就是個水題,注意乘法會爆longlong,用快速乘就可以了
#include<iostream> #include<string> #include<map> using namespace std; typedef long long ll; const ll mod = 1LL << 47; ll quick_mul(ll a, ll b) { ll ans = 0; while (b) { if (b & 1)ans = (ans + a) % mod; a = (a + a) % mod; b >>= 1; } return ans; } int main() { string a, b, c; ll t; map<string, ll> m; while (cin >> a >> b) { if (a == "def") { cin >> t; m[b] = t; } else { cin >> c; if (a == "add")m[b] = (m[b] + m[c]) % mod; else if (a == "sub")m[b] = (m[b] - m[c] + mod) % mod; else if (a == "mul")m[b] = quick_mul(m[b], m[c]); else if (a == "div")m[b] = m[b] / m[c]; else m[b] %= m[c]; } cout << b << " = " << m[b] << endl; } return 0; }
D:Number theory
這題其實如果模一個質數就變成水題了,這題其實是一個線段樹的點修改+區間查詢,M操作可以看成把i節點修改成 yi,N操作可以看作吧di節點修改成1,然後每次的乘積就是區間1-10的乘積,如果看出來是線段樹,其實只是個模板題
#include<iostream> #include<cstring> typedef long long ll; const int N = 400005; ll mul[N], M; void build(int rt, int L, int R) { if (L == R) { mul[rt] = 1; return; } int mid = (L + R) >> 1; build(rt << 1, L, mid); build(rt << 1 | 1, mid + 1, R); mul[rt] = mul[rt << 1] * mul[rt << 1 | 1] % M; } void update(int rt, int L, int R, const int &pos, const int &val) { if (L == R) { mul[rt] = val; return; } int mid = (L + R) >> 1; if (pos <= mid)update(rt << 1, L, mid, pos, val); else update(rt << 1 | 1, mid + 1, R, pos, val); mul[rt] = mul[rt << 1] * mul[rt << 1 | 1] % M; } int main() { int T, Q, x; scanf("%d", &T); char s[2]; while (T--) { scanf("%d%lld", &Q, &M); build(1, 1, Q); for (int i = 1; i <= Q; ++i) { scanf("%s%d", s, &x); if (s[0] == 'M')update(1, 1, Q, i, x); else update(1, 1, Q, x, 1); printf("%lld\n", mul[1]); } } return 0; }
E:Traffic jam
這題其實是一個最短路徑,注意鬆弛不再是if(dis[v]>dis[now]+edge[now][i].second)了,要看能不能走(x=dis[now]/a[now],能走的話x%2==0),能走才是dis[now],不能的話,dis[now]要改成(x+1)*a[now],其他的就是正常的最短路徑,要注意a[now]可以為0,這時候不能除,不知為什麼當時這題沒有A出來
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int N = 1001, inf = 0x3f3f3f3f;
vector<pair<int, int> > edge[N];
int dis[N], a[N], s, t;
void dijkstra() {
priority_queue<pair<int, int> >q;
dis[s] = 0;
q.push(make_pair(0, s));
while (!q.empty()) {
int now = q.top().second;
int cost = dis[now];
if (a[now]) {
int x = dis[now] / a[now];
if (x & 1)cost = (x + 1)*a[now];
}
if (now == t)return;
q.pop();
for (int i = 0; i < edge[now].size(); ++i) {
int v = edge[now][i].first;
if (dis[v] > cost + edge[now][i].second) {
dis[v] = cost + edge[now][i].second;
q.push(make_pair(-dis[v], v));
}
}
}
}
int main() {
int T, n, m, x, y, z;
scanf("%d", &T);
while (T--) {
for (int i = 0; i < N; ++i)edge[i].clear();
memset(dis, inf, sizeof(dis));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
for (int i = 0; i < m; ++i) {
scanf("%d%d%d", &x, &y, &z);
edge[x].push_back(make_pair(y, z));
edge[y].push_back(make_pair(x, z));
}
scanf("%d%d", &s, &t);
dijkstra();
printf("%d\n", dis[t]);
}
return 0;
}
F:IoU
這題其實是最簡單的一個題目,分別求出長和寬就可以了
#include<iostream>
typedef long long ll;
ll min(const ll &a, const ll &b) { return a <= b ? a : b; }
ll max(const ll &a, const ll &b) { return a >= b ? a : b; }
int main() {
int x1, y1, w1, h1, x2, y2, w2, h2, T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d%d%d%d%d%d", &x1, &y1, &w1, &h1, &x2, &y2, &w2, &h2);
ll overlap = 0, unionArea = w1 * h1 + w2 * h2;
if (min(x1 + w1, x2 + w2) > max(x1, x2) && min(y1 + h1, y2 + h2) > max(y1, y2))
overlap = (min(x1 + w1, x2 + w2) - max(x1, x2))*(min(y1 + h1, y2 + h2) - max(y1, y2));
unionArea -= overlap;
if (!unionArea)printf("0.00\n");
else printf("%.2lf\n", 1.0*overlap / unionArea);
}
return 0;
}
G:Chosen by god
這個奧數飛彈很騷,我當時根本沒看出來,導致沒看懂題目,其實就說攻擊分裂成n份,每次攻擊都是隨機的,
那麼也就是說n<m時為0,否則答案是
這題用楊輝三角+字尾和+費馬小定理求逆元
#include<iostream>
typedef long long ll;
const int N = 1001, mod = 1e9 + 7;
ll c[N][N] = { 1 }, inv[N] = { 1 };
ll quickPow(ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1)ans = ans * a%mod;
a = a * a%mod;
b >>= 1;
}
return ans;
}
int main() {
int T, n, m;
ll t = 2;
for (int i = 1; i < N; ++i) {
inv[i] = quickPow(t, mod - 2);
t = t * 2 % mod;
}
for (int i = 1; i < N; ++i)
for (int j = 0; j <= i; ++j)c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
for (int i = 1; i < N; ++i)
for (int j = i - 1; j >= 0; --j)c[i][j] = (c[i][j] + c[i][j + 1]) % mod;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
if (m == 0)printf("1\n");
else printf("%lld\n", c[n][m] * inv[n] % mod);
}
return 0;
}
I:Mind control
這題其實期望很好推,但是要化簡,當時不會化
m+1就有逆元了,用逆元遞推的,不然會wa(不知道為什麼)
#include<iostream>
typedef long long ll;
const int mod = 1e9 + 7, N = 1e6 + 5;
ll inv[N] = { 0,1 };
int main(){
for (int i = 2; i < N; i++)inv[i] = (mod - mod / i)*inv[mod%i] % mod;
int T, n, m;
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
printf("%lld\n", n <= m ? n : 1LL * m*(n + 1) % mod*inv[m + 1] % mod);
}
return 0;
}
剩下的題目會了再說吧