目錄
- 946(div3)
- A
- B
- C
- D
- E
- G
946(div3)
A
每個螢幕3X5, 可放2個2X2, 其餘可填7個1X1 先算2X2需要多少個螢幕,再算當前螢幕是否可放下所有1X1, 根據1X1的量加螢幕.
void func()
{
int a,b;
cin >> a >> b;
int ans = (b+1) / 2;
int stp = a - (ans*15 - 4*b);
if(stp > 0) ans += (stp + 14) / 15;
//當前螢幕是否可以放下所有a,不能則再加螢幕.
cout << ans << '\n';
}
B
s為原字串,r為s中字字母去重排序後字串串. 題目需要求將s中每個字元根據r替換為r中對稱字元後的字串的原字串. 實際操作可逆, 從s'得到s和從s得到s'的操作是一樣的.
用s將r處理出來,再把對稱字元互相對映用二分硬找也行,也是O(nlogn),輸出替換的串即可.
void func()
{
int n;
string st;
cin >> n >> st;
vector<char> a(n);
for(int i=0;i<n;++i) a[i] = st[i];
sort(a.begin(),a.end());//處理r
a.erase(unique(a.begin(),a.end()),a.end());//去重函式
int len = a.size();
map<char,char> mp;//對稱串對映
for(int i=0;i<len;++i)
{
mp[a[i]] = a[len - 1 - i];
}
for(int i=0;i<n;++i)
{
st[i] = mp[st[i]];
}
cout << st << '\n';
}
C
資料最多可有1011, 要開longlong
解法1: 3map暴力O(nlogn)
找到連續三個字元的字串中有一個不同的個數. 時間長且資料不大,開三個map分別存123號位不同的點即可. 統計其中兩個位相同第三個位相同和不同的數目,相乘加入總數即可.
程式碼比較醜陋, 直接貼上了三個for().
void func()
{
int n;
cin >> n;
vector<int> v(n);
map<pair<int,int>,vector<int>> mp1,mp2,mp3;//三個二元組對映vector, 根據其中兩位對映第三位.
int a,b,c;
for(auto &i : v) cin >> i;
for(int i=0;i<n-2;++i)
{
mp1[{v[i],v[i+1]}].push_back(v[i+2]);// 儲存
mp2[{v[i],v[i+2]}].push_back(v[i+1]);
mp3[{v[i+1],v[i+2]}].push_back(v[i]);
}
int cnt = 1,ans = 0;
for(auto &i : mp1)//每次處理一組兩位置相同的元素
{
sort(i.y.begin(),i.y.end());//排序,方便根據前一位統計有幾個相同
int size = i.y.size();
for(int j=1;j<size;++j)
{
if(i.y[j] == i.y[j-1]) cnt++;
else
{
ans += (size-cnt)*cnt;
cnt = 1;
}
}
ans += (size-cnt)*cnt;//可能最後一元素與前一個相同, 需單獨處理
cnt = 1;
}
//後二for()操作相同
for(auto &i : mp2)
{
sort(i.y.begin(),i.y.end());
int size = i.y.size();
for(int j=1;j<size;++j)
{
if(i.y[j] == i.y[j-1]) cnt++;
else
{
ans += (size-cnt)*cnt;
cnt = 1;
}
}
ans += (size-cnt)*cnt;
cnt = 1;
}
for(auto &i : mp3)
{
sort(i.y.begin(),i.y.end());
int size = i.y.size();
for(int j=1;j<size;++j)
{
if(i.y[j] == i.y[j-1]) cnt++;
else
{
ans += (size-cnt)*cnt;
cnt = 1;
}
}
ans += (size-cnt)*cnt;
cnt = 1;
}
cout << ans/2 << '\n';
}
解法2: 容斥O(nlogn)
統計出12,13,23號位相同的可能性, 再減去123號位完全相同的可能.
注意: 在12,13,23三組,統計了三次123相同,所以最後結果需要減三個123.
容斥原理
void func()
{
int n,ans1 = 0,ans2 = 0;
cin >> n;
map<pair<int,int>,int> cnt1,cnt2,cnt3;//分別存12, 13, 23相同的元素數目
map<edge,int> cnt4;// 存三個元素完全相同的元素數目
vector<int> v(n);
for(int i=0;i<n;++i) cin >> v[i];
for(int i=0;i<n-2;++i)
{
cnt1[{v[i],v[i+1]}]++;
cnt2[{v[i],v[i+2]}]++;
cnt3[{v[i+1],v[i+2]}]++;
cnt4[{v[i],v[i+1],v[i+2]}]++;
}
for(auto &i : cnt1) ans1 += (i.y-1)*i.y/2;
for(auto &i : cnt2) ans1 += (i.y-1)*i.y/2;
for(auto &i : cnt3) ans1 += (i.y-1)*i.y/2;
for(auto &i : cnt4) ans2 += i.y*(i.y-1)/2;
int ans = (ans1 - 3*ans2);
cout << ans << '\n';
}
D
保證兩者都進行過運動下直接暴力即可, 因為多個if太長了, 抄了會長的程式碼.
對於每種操作, 第一次給了H, 第二次就必須給R. 所以計算同類操作MOD2後是否相同來確定此方向下HR同位置. 另外, 將Y軸操作初試設為1, X軸操作設為0可以保證有解情況下兩者一定進行了操作.
void func()
{
int n;
string st,ans;
cin >> n >> st;
int cnt = 0;// 確定都進行操作
map<char,int> mp;
mp['W'] = mp['E'] = 1;
mp['N'] = mp['S'] = 0;
for(int i=0;i<n;++i)
{
ans += (mp[st[i]] == 0 ? 'R' : 'H');
if(ans[i] == 'R') cnt |= 1;// R
if(ans[i] == 'H') cnt |= 2;// H
mp[st[i]] ^= 1;
}
/*
cnt | 1 | 2 == 3, 保證兩者都進行操作
N == S, W == E, 保證位置相同
*/
if(cnt == 3 && mp['N'] == mp['S'] && mp['W'] == mp['E']) cout << ans << '\n';
else cout << "NO\n";
}
E
G
貪心, 如果當前金錢可以購買本月快樂, 則加入答案, 但是如果買本次快樂的錢足夠買後面兩次的, 那就不成立, 所以在遇到後續更便宜的快樂時替換掉, 總快樂不變但是餘額變多.
其實是反悔貪心, 把每次加入的答案加入堆, 如果餘額足夠則加入, 不足則和堆最大值比較, 如果更小則替換最大值, 獲得差值的金錢.
void func()
{
int m,x;
cin >> m >> x;
vector<int> a(m);
for(auto &i :a) cin >> i;
priority_queue<int> pq;// 大根堆
int money = 0,happy = 0;
for(int i=0;i<m;++i)
{
if(money >= a[i])
{
happy++;
money -= a[i];
pq.push(a[i]);
}
else if(pq.size() && pq.top() > a[i])// 注意堆為空時不能訪問.
{
money += (pq.top()-a[i]);
pq.pop();
pq.push(a[i]);
}
money += x;
}
cout << happy << '\n';
}