本場鏈接: CodeChef Starters 6 Division 3 (Rated)
Cricket Ranking
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int r1,r2,w1,w2,c1,c2;scanf("%d%d%d%d%d%d",&r1,&w1,&c1,&r2,&w2,&c2);
int cnt = 0;
if(r1 > r2) ++cnt;
if(w1 > w2) ++cnt;
if(c1 > c2) ++cnt;
if(cnt >= 2) puts("A");
else puts("B");
}
return 0;
}
Three Dices
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int x,y;scanf("%d%d",&x,&y);
if(x + y >= 6) puts("0");
else printf("%.18lf\n",(6 - x - y) / 6.0);
}
return 0;
}
Joker and Batman
把各個元素標記一下來自哪個集合,再處理最長段即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 1e5+7;
int from[N],a[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,m,l;scanf("%d%d%d",&n,&m,&l);
forn(i,1,m)
{
int k;scanf("%d",&k);
while(k--)
{
int x;scanf("%d",&x);
from[x] = i;
}
}
forn(i,1,l) scanf("%d",&a[i]);
int res = 0;
forn(i,1,l)
{
int j = i;
while(j + 1 <= l && from[a[j + 1]] == from[a[i]]) ++j;
++res;
i = j;
}
printf("%d\n",res);
}
return 0;
}
Even tuples
首先數值沒有意義,直接將所有元素踢成1/0
.
其次注意到要使一段結果是偶數只有兩種情況:三個元素全為偶數,或者三個元素中有兩個是奇數.考慮直接組合:前者情況相當於在區間中所有的偶數里選出三個;后者相當於先從奇數里選出兩個,剩下一個偶數可以在區間里任選,方案數由乘法原理相乘合並.
由於選出的數只有1/2/3
所以可以直接把表達式寫出來.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 1e5+7;
int a[N],c[N][2];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,q;scanf("%d%d",&n,&q);
forn(i,1,n) scanf("%d",&a[i]);
forn(i,1,n)
{
c[i][0] = c[i - 1][0];c[i][1] = c[i - 1][1];
++c[i][a[i] % 2];
}
while(q--)
{
int l,r;scanf("%d%d",&l,&r);
ll res = 0;
int u0 = c[r][0] - c[l - 1][0],u1 = c[r][1] - c[l - 1][1];
if(u0 >= 3) res = (res + 1ll * u0 * (u0 - 1) * (u0 - 2) / 6);
if(u0 >= 1 && u1 >= 2) res = (res + 1ll * u1 * (u1 - 1) / 2 * u0);
printf("%lld\n",res);
}
}
return 0;
}
Chefs Homework Dilemma
因為每天只有兩種選擇:要么做要么不做.考慮dp
:
- 狀態:\(f[i]\)表示前\(i\)天的最少時間.但是有個問題,因為這樣表示狀態不能通過前面的已知狀態得到這個人上一次做作業是什么時候:可以如此修改狀態表示:\(f[i]\)表示前\(i\)天且強制\(i\)天時做作業的最少時間.
- 入口:\(f[0] = 0\).
- 轉移:\(f[i] = \min\{f[i - k - 1],f[i - k]...f[i - 1]\} + H[i]\).
- 出口:\(f[n + 1]\).因為\(f[n]\)這個狀態沒有包含:第\(n\)天的時候不做作業的情況,所以不妨做到第\(n+1\)天並強制\(H[n + 1] = 0\).
由於這個題卡的比較緊,數據范圍是\(10^6\)且只有0.5s
,所以直接套RMQ可能不太穩,一種穩定線性的做法是:使用單調隊列維護前面一個長度為\(k+1\)的區間,保證隊頭是最小值.如此可以使得整個轉移是線性的.
注意維護區間的長度是\(k+1\)而不是\(k\).
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 1e6+7;
int H[N],f[N],q[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,k;scanf("%d%d",&n,&k);
forn(i,1,n) scanf("%d",&H[i]);H[n + 1] = 0;
int hh = 0,tt = 0;q[0] = 0;f[0] = 0;
forn(i,1,n + 1)
{
while(hh <= tt && i - q[hh] > k + 1) ++hh;
f[i] = f[q[hh]] + H[i];
while(hh <= tt && f[q[tt]] >= f[i]) --tt;
q[++tt] = i;
}
printf("%d\n",f[n + 1]);
}
return 0;
}
Palindromic number
(其實感覺想的難度還沒上一個題目高)
題目其實是可以看做有兩問的:是否可以變成一個回文串以及能做出來的回文串字典序最大的方案.
考慮(1)問:枚舉每個字符對,找到一個\(t\)使得:從\(s[i]\)轉移到\(t\)加上\(s[n - i + 1]\)轉移到\(t\)的代價和最小.可以聯想到題目的轉換操作其實等價於在圖上移動點,從起點走到終點對應把起點的字符轉換為終點的字符的過程,邊權之和就是代價.所以按題目所給的條件建邊,求出任意兩對元素之間的最短路即可等價於轉換到\(t\)的代價.這個部分可以丟個floyd
跑.
考慮(2)問:因為字典序是從前往后一個一個看的,所以一個經典的策略就是從前往后一個一個看能不能搞個最大的來.具體來說:從前往后遍歷,倒着枚舉目標字符看能不能做出來,如果可以搞就直接替換並把代價扣過去,但是如果在(1)的時候把s
修改成了一個合法的回文串,在(2)直接做就會出錯,但是直接做(2)這樣的貪心又不好保證至少是一個回文串.
問題是,如果記s'
為回文串的話((1)問得到的),那么從s[i] -> s'[i] -> target
的路徑不一定會比s[i] -> target
更短,所以在做(2)問的時候,代價應該是:s->target
的代價減去s->s'
的代價.相當於是把(1)問倒回去再計算.這樣做就可以保證最短路的正確性.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define int ll
const int N = 1e5+7,M = 12;
int dist[M][M];
char s[N],P[N];
int n,m,k;
inline int gcost(int p,int t,char* s)
{
int x = s[p] - '0',y = s[n - p + 1] - '0';
if(p == n - p + 1) return dist[x][t];
return dist[x][t] + dist[y][t];
}
signed main()
{
int T;scanf("%lld",&T);
while(T--)
{
forn(i,0,9) forn(j,0,9) dist[i][j] = 1e18;
forn(i,0,9) dist[i][i] = 0;
scanf("%lld%lld%lld",&n,&m,&k);
scanf("%s",s + 1);forn(i,1,n) P[i] = s[i];
forn(i,1,m)
{
int x,y,k;scanf("%lld%lld%lld",&x,&y,&k);
dist[x][y] = min(dist[x][y],k);
}
forn(k,0,9) forn(i,0,9) forn(j,0,9) dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j]);
bool ok = 1;
forn(i,1,(n + 1) / 2)
{
if(s[i] == s[n - i + 1]) continue;
int mint = 0,mincost = gcost(i,0,s);
forn(t,1,9) if(gcost(i,t,s) < mincost) mincost = gcost(i,t,s),mint = t;
if(k < mincost)
{
ok = 0;
break;
}
s[i] = s[n - i + 1] = mint + '0';
k -= mincost;
}
if(!ok) puts("-1");
else
{
forn(i,1,(n + 1) / 2)
{
forr(t,s[i] - '0' + 1,9)
{
int cost = gcost(i,t,P) - gcost(i,s[i] - '0',P);
if(k >= cost)
{
k -= cost;
s[i] = s[n - i + 1] = t + '0';
break;
}
}
}
printf("%s\n",s + 1);
}
}
return 0;
}