A. 簽到
簡單C語言模擬題。
#include <stdio.h>
double a[5] = {0.01,0.05,0.15,0.3};
int n,p[5];
int main() {
scanf("%d", &n);
for(int i=0;i<4;i++)
p[i] = 386 * a[i];
if(n<=p[0]) printf("Winner!");
else if(n<=p[1]) printf("Au!");
else if(n<=p[2]) printf("Ag!");
else if(n<=p[3]) printf("Cu!");
else printf("Fe...");
return 0;
}
B. 切蛋糕
由於負鼠自己也要吃蛋糕,所以剛開始要 n+1 。
很容易看出當 n 是2的倍數的情況下,需要切n/2刀,否則至少切 n 刀。
#include <stdio.h>
int T,n;
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d",&n);
n++;
if(n==1) printf("0\n");
else if(n%2==0) printf("%d\n",n/2);
else printf("%d\n",n);
}
return 0;
}
C. 移動字符串
簡單模擬。
注意,強行模擬 k 次必然超時。可以發現只有當前一個字母全都刪完了後,纔會刪後一個字母,因此,我們可以將k次刪除操作簡化完成。
#include <cstdio>
#include <iostream>
int n, k, ds[30];
using namespace std;
int main() {
string s;
cin >> n >> k >> s;
for (int i = 0; i < s.length(); i++) ds[s[i] - 'a']++;
for (int i = 0; i < 26; i++)
if (k > ds[i])
k -= ds[i];
else
ds[i] = k, k = 0;
for (int i = 0; i < s.length(); i++)
ds[s[i] - 'a'] ? ds[s[i] - 'a']-- : putchar(s[i]);
return 0;
}
D. 卡布奇諾
數論+位運算。
每個數乘2或者除2,我們可以通過位運算枚舉出每一個數 可以轉變成的數, 以及變成這個數所需要的轉換次數。
用 mp[i] 儲存可以轉變到的數 的次數。
因此最後我們只要找到最大的 ,使得 ,然後枚舉這個 的2的冪次方倍(必然滿足條件),枚舉得到所要求的最小值。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
template <class T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,a[maxn],maxi;
int mp[maxn];
int cal_one(int a,int x) {
int res = 0;
while(x%a) {
a /= 2;
res++;
}
while(x>a) {
a *= 2;
res++;
}
return res;
}
int calc(int x) {
int res = 0;
for(int i=0;i<n;i++) {
res += cal_one(a[i],x);
}
return res;
}
int main() {
read(n);
for(int i=0;i<n;i++) {
read(a[i]);
ll f = a[i];
while(f) {
mp[f]++;
f /= 2;
}
}
int ans = 1e9;
for(int i=100000;i>=1;i--) {
if(mp[i]>=n) {
maxi = i;
break;
}
}
for(int i=maxi;i<=100000;i*=2) {
ans = min(ans,calc(i));
}
cout << ans << endl;
return 0;
}
E. 花錢
DFS + 簡單組合數學。
顯然這個圖是一棵樹,可以將圖中任意一個節點作爲樹根建樹。
那麼可以將y作爲樹根,x則是樹y的某一個節點,
設dep[i]爲子樹i的size(包括i本身),節點z爲(y,x)路徑上y的兒子節點
符合條件的對數爲(y,x)路徑兩端的節點數相乘,即dep[x]*(dep[y]-dep[z]),用 減去就可以得出答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 300005;
template <class T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
ll n,s,t,cnt1,cnt2,dep[maxn],fa[maxn];
vector<int> e[maxn];
bool vis[maxn];
void dfs(int x) {
vis[x] = 1;
for(auto i:e[x]) {
if(!vis[i]) {
fa[i] = x;
dfs(i);
dep[x] += dep[i];
}
}
}
int main() {
read(n), read(s), read(t);
for(int i=0,x,y;i<n-1;i++) {
read(x), read(y);
e[x].push_back(y);
e[y].push_back(x);
}
for(int i=1;i<=n;i++) dep[i]=1;
dfs(s);
ll ans = n*(n-1);
for(int i=t;;i=fa[i]) {
if(fa[i]==s) {
cnt1 = dep[s]-dep[i];
break;
}
}
ans -= cnt1 * dep[t];
cout << ans << endl;
return 0;
}
F. 下棋
博弈論。
如果 CY
在 叉JJ
的左上方的話, CY
必勝;
同理,如果Cy
在 叉JJ
的右下方的話, CY
必敗;
然後我們來考慮剩下的情況,由於 叉JJ
可以斜着走,因此,她回到 點至少需要移動 次,而CY
則需要移動 次。如果 , 那麼叉JJ
必然可以以最短路提前到達終點或堵住 CY
的路, 此時 CY
必敗, 否則 CY
必勝。
#include <bits/stdc++.h>
using namespace std;
int t,a,b,c,d;
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d%d%d",&a,&b,&c,&d);
if(a<=c&&b<=d) {puts("CY");}
else if(a>=c && b>=d) {puts("XJJ");}
else {
int f = a+b;
int g = max(c,d);
if(f<=g) puts("CY");
else puts("XJJ");
}
}
return 0;
}
G. 學Python
動態規劃問題。
dp[i][j] 表示第i行的縮進有j個Tab時的合法方案數。
-
第i行爲’f’,則第i+1行的縮進只能爲j+1。
dp[i+1][j+1] = dp[i][j]
-
第i行爲’s’,則第i+1行的縮進可以爲[0,j]中的任意一種。
dp[i+1][j] += dp[i][0 to j]
最終結果即爲
#include <bits/stdc++.h>
using namespace std;
const int mo = 1e9 + 7;
int dp[5005][5005];
int main() {
int n;
cin >> n;
dp[1][0] = 1;
char s;
for (int i = 1; i <= n; i++) {
cin >> s;
if(s=='f') {
for(int j=0;j<n;j++)
dp[i+1][j+1] = dp[i][j];
} else {
int sum = 0;
for(int j=n-1;j>=0;j--) {
sum = (sum+dp[i][j]) % mo;
dp[i+1][j] = sum;
}
}
}
int sum = 0;
for (int i = 0; i < n; i++) {
sum = (sum + dp[n][i]) % mo;
}
cout << sum << endl;
return 0;
}
H. Sort the String
線段樹 + 計數排序。
由於字母只有26個,所以在給區間[l,r]排序的時候,只需要統計出每個字母的數量,然後再排序。
底下問題就來到了怎麼統計數量以及更新數量。我們可以通過建立26棵線段樹,每棵線段樹維護一個字母在一段區間上的數量。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n,q;
char s[maxn];
struct SegmentTree {
int t[maxn*4], lazy[maxn*4];
void push_up(int p) {
t[p] = t[p<<1] + t[p<<1|1];
}
void push_down(int p,int l,int r) {
if(lazy[p]!=-1) {
int mid = (l+r) / 2;
t[p<<1] = (mid-l+1) * lazy[p];
t[p<<1|1] = (r-mid) * lazy[p];
lazy[p<<1] = lazy[p<<1|1] = lazy[p];
lazy[p] = -1;
}
}
void build(int p,int l,int r,char c) {
t[p] = 0, lazy[p] = -1;
if(l==r) {
t[p] = (s[l]==c);
return;
}
int mid = (l+r) / 2;
build(p<<1,l,mid,c);
build(p<<1|1,mid+1,r,c);
push_up(p);
}
// [L,R] 爲查詢的區間, [l,r] 爲當前區間
void update(int p,int l,int r,int L,int R,int val) {
if(L<=l && R>=r) {
t[p] = (r-l+1) * val;
lazy[p] = val;
return;
}
push_down(p,l,r);
int mid = (l+r) / 2;
if(L<=mid) update(p<<1,l,mid,L,R,val);
if(R>mid) update(p<<1|1,mid+1,r,L,R,val);
push_up(p);
}
int query(int p,int l,int r,int L,int R) {
int res = 0;
if(L<=l && R>=r) return t[p];
push_down(p,l,r);
int mid = (l+r) / 2;
if(L<=mid) res += query(p<<1,l,mid,L,R);
if(R>mid) res += query(p<<1|1,mid+1,r,L,R);
push_up(p);
return res;
}
void print(int p,int l,int r,char c) {
if(l==r) {
if(t[p]) s[l] = c;
return;
}
push_down(p,l,r);
int mid = (l+r) / 2;
if(l<=mid) print(p<<1,l,mid,c);
if(r>mid) print(p<<1|1,mid+1,r,c);
push_up(p);
}
}tr[30];
int cnt[30];
int main() {
scanf("%d%d%s",&n,&q,s+1);
for(int i=0;i<26;i++)
tr[i].build(1,1,n,i+'a');
for(int i=0,l,r,op;i<q;i++) {
scanf("%d%d%d",&l,&r,&op);
memset(cnt,0,sizeof(cnt));
for(int i=0;i<26;i++)
cnt[i] = tr[i].query(1,1,n,l,r);
for(int i=0;i<26;i++)
tr[i].update(1,1,n,l,r,0);
if(!op) {
for(int i=25;i>=0;i--) {
if(!cnt[i]) continue;
tr[i].update(1,1,n,l,l+cnt[i]-1,1);
l += cnt[i];
}
} else {
for (int i=0;i<26;i++) {
if(!cnt[i]) continue;
tr[i].update(1,1,n,l,l+cnt[i]-1,1);
l += cnt[i];
}
}
}
for(int i=0;i<26;i++)
tr[i].print(1,1,n,i+'a');
for(int i=1;i<=n;i++) cout << s[i];
return 0;
}
I. BJ的貓餅
首先我們注意到, 與 都是 的冪次,因此, 等價於 ,因此,我們僅需要計算 的最大值即可
以下部分需要讀者掌握初步的網絡流以及費用流知識,若從未接觸過,請首先出門左轉 Google
好啦相信聰明的讀者已經學會了網絡流和費用流。
本題套用最小費用最大流模板,需要注意的是,本題實際上求的是最大費用最大流,因此給費用加負號再求最小費用即可。求出來的最小費用再加一個負號就是原來的最大價值之和。
我們需要的點有:源點,匯點,超級匯點, 個點表示 種普通貓餅, 個點表示 種超級貓餅。
從超級源點向每個普通貓餅建邊,容量爲 ,費用爲 。普通貓餅向匯點建邊,容量爲 ,費用爲 。再從每種可以製成超級貓餅的普通貓餅,向相對應的超級貓餅建邊,容量爲 ,費用爲 。這些超級貓餅再向匯點建邊,容量爲 ,費用爲 。
最後,匯點向超級匯點建邊,容量爲 ,費用爲 。
#include <bits/stdc++.h>
const int maxn= 2e3+50;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007;
using namespace std;
struct edge{
int to, cap, cost, rev;
};
vector<edge>G[maxn];
int dis[maxn];
int pre[maxn], prid[maxn];
int h[maxn];
void init() {
for (int i = 0; i < maxn; ++i)
G[i].clear();
memset(pre, 0, sizeof(pre));
memset(prid, 0, sizeof(prid));
memset(h, 0, sizeof(h));
}
void add_edge(int from, int to, int cap, int cost) {
G[from].push_back({to, cap, cost, G[to].size()});
G[to].push_back({from, 0, -cost, G[from].size()-1});
}
void dijkstra(int s) {
struct point_dis {
int point;
int val;
bool operator < (const point_dis &pd) const {
return val > pd.val;
}
};
fill(dis, dis+maxn, INF);
dis[s] = 0;
priority_queue<point_dis>q;
q.push({s, dis[s]});
while (!q.empty()) {
int now = q.top().point;
q.pop();
for (int i = 0; i < G[now].size(); ++i) {
edge &e = G[now][i];
if (e.cap > 0 && dis[e.to] > dis[now] + e.cost + h[now] - h[e.to]) {
dis[e.to] = dis[now] + e.cost + h[now] - h[e.to];
pre[e.to] = now;
prid[e.to] = i;
q.push({e.to, dis[e.to]});
}
}
}
}
pair<int, int> min_cost_max_flow(int s, int t) {
int flow = 0;
int cost = 0;
while (true) {
dijkstra(s);
if (dis[t] == INF)
return {flow, cost};
for (int i = 0; i < maxn; ++i) {
h[i] += dis[i];
}
int d = INF;
for (int v = t; v != s; v = pre[v]) {
d = min(d, G[pre[v]][prid[v]].cap);
}
flow += d;
cost += d*h[t];
for (int v = t; v != s; v = pre[v]) {
G[pre[v]][prid[v]].cap -= d;
G[v][G[pre[v]][prid[v]].rev].cap += d;
}
}
}
long long quick_pow(int t) {
long long res = 1;
long long base = 2;
while (t) {
if (t & 1)
res = (res*base) % mod;
base = (base*base) % mod;
t >>= 1;
}
return res;
}
int main() {
std::ios::sync_with_stdio(false);
int n, m, c;
while (cin >> n >> m >> c) {
init();
int start_point = 0, meeting_point = 2048, end_point = 2049;
for (int i = 1; i <= n; ++i) {
int amount;
cin >> amount;
add_edge(start_point, i, amount, 0);
}
for (int i = 1; i <= n; ++i) {
int value, volume;
cin >> value >> volume;
value = log2(value);
add_edge(i, meeting_point, volume, -value);
}
for (int i = 1; i <= m; ++i) {
int k;
cin >> k;
while (k--) {
int id;
cin >> id;
add_edge(id, 1e3+i, INF, 0);
}
int value, volume;
cin >> value >> volume;
value = log2(value);
add_edge(1e3+i, meeting_point, volume, -value);
}
add_edge(meeting_point, end_point, c, 0);
pair<int, int>ans = min_cost_max_flow(start_point, end_point);
cout << quick_pow(-ans.second) << endl;
}
return 0;
}