Problem A
分三種情況:
- 并且臊恋,由于必定有解,所以必定不會跟相等墓捻。所以直接輸出和即可抖仅。
- 除此之外,如果砖第,那么輸出和即可撤卢。
- 否則,直接輸出和即可梧兼。
時間復(fù)雜度為
#include <iostream>
using namespace std;
int main()
{
int q;
cin >> q;
while(q--)
{
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
if((l1 == l2) && (l1 == r2))
{
cout << r1 << " " << l1 << endl;
}
else if(l1 == l2)
{
cout << l1 << " " << r2 << endl;
}
else
{
cout << l1 << " " << l2 << endl;
}
}
return 0;
}
Problem B
由于必定有解放吩,于是對數(shù)組做兩次如下操作:
- 選出數(shù)組中最大的數(shù)字,記為
- 將數(shù)組中所有的因數(shù)去掉一份(去掉一份你的意思是羽杰,假設(shè)數(shù)組里有兩個1渡紫,那么只去掉一個)到推。
數(shù)組用map
統(tǒng)計每個數(shù)的個數(shù)來存儲,找因數(shù)使用根號試除法惕澎,則時間復(fù)雜度為
#include <iostream>
#include <map>
using namespace std;
int getAns(map<int, int> &mp)
{
auto it = mp.end();
it--;
int ret = it->first;
for(int i = 1; i * i <= ret; i++)
{
if(ret % i)continue;
mp[i]--;
if(mp[i] == 0)
{
mp.erase(i);
}
if(ret / i != i)
{
mp[ret / i]--;
if(mp[ret / i] == 0)
{
mp.erase(ret / i);
}
}
}
return ret;
}
int main()
{
int n;
while(cin >> n)
{
map<int, int> mp;
for(int i = 0; i < n; i++)
{
int p;
cin >> p;
mp[p]++;
}
cout << getAns(mp) << " ";
cout << getAns(mp) << endl;
}
return 0;
}
Problem C
根據(jù)題意顯然可以得知莉测,符合題目要求的字符串必定是以3為周期重復(fù)的。
所以只需要枚舉前三個字母集灌,然后構(gòu)造跟輸入的字符串s等長的字符串,再比較其字母不同的個數(shù)复哆,最后從這些值中取最小值即可欣喧。
時間復(fù)雜度為
#include <iostream>
#include <string>
#include <map>
using namespace std;
const string AlphaList = "RGB";
int getAns(string &c, string &s)
{
int ret = 0;
for(int i = 0; i < s.length(); i++)
{
if(s[i] != c[i % 3])
{
ret++;
}
}
return ret;
}
int main()
{
int n;
while(cin >> n)
{
string s;
cin >> s;
int ans = s.length() + 5;
string c;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(i == j)continue;
for(int k = 0; k < 3; k++)
{
if(i == k)continue;
if(j == k)continue;
string pc;
pc += AlphaList[i];
pc += AlphaList[j];
pc += AlphaList[k];
int pans = getAns(pc, s);
if(ans > pans)
{
ans = pans;
c = pc;
}
}
}
}
cout << ans << endl;
for(int i = 0; i < s.length(); i++)
{
cout << c[i % 3];
}
cout << endl;
}
return 0;
}
Problem D
設(shè)為字符串為原串的子串的情況下,且最后一個字符強(qiáng)制改為的情況下的最小修改數(shù)梯找。然后另設(shè)一個數(shù)組用于記錄dp過程中的選擇唆阿,然后直接dp過去就行了。
時間復(fù)雜度為
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const string AlphaList = "RGB";
int dp[200005][4];
int prt[200005][4];
void Solve(string &s)
{
for(int i = 0; i < 3; i++)
{
if(s[0] == AlphaList[i])
{
dp[0][i] = 0;
}
else
{
dp[0][i] = 1;
}
}
for(int i = 1; i < s.length(); i++)
{
for(int j = 0; j < 3; j++)
{
dp[i][j] = s.length() + 5;
for(int k = 0; k < 3; k++)
{
if(j == k)continue;
if(dp[i][j] > dp[i-1][k])
{
dp[i][j] = dp[i-1][k];
prt[i][j] = k;
}
}
if(s[i] != AlphaList[j])
{
dp[i][j]++;
}
}
}
int ans = 0;
if(dp[s.length() - 1][1] < dp[s.length() - 1][ans])
{
ans = 1;
}
if(dp[s.length() - 1][2] < dp[s.length() - 1][ans])
{
ans = 2;
}
cout << dp[s.length() - 1][ans] << endl;
string ansstr;
for(int i = s.length() - 1; i >= 0; i--)
{
ansstr += AlphaList[ans];
ans = prt[i][ans];
}
reverse(ansstr.begin(), ansstr.end());
cout << ansstr << endl;
}
int main()
{
int n;
while(cin >> n)
{
string s;
cin >> s;
Solve(s);
}
}
Problem E (E1 + E2)
首先顯然需要建立兩顆支持區(qū)間更新的線段樹锈锤,分別維護(hù)數(shù)組的最小值和最大值驯鳖。
能夠想到的一個思路是枚舉數(shù)組中的每個數(shù),令其盡可能小久免,除它以外的數(shù)盡可能大(只需要只選取所有包含當(dāng)前數(shù)的區(qū)間就可以達(dá)到這個目的來)浅辙,然后得到此時的極差,并取所有極差的最大值阎姥。
對于每個枚舉的值計算極差的時間復(fù)雜度是记舆,于是總的時間復(fù)雜度是。這顯然爆炸呼巴。
換個思路泽腮,考慮到相鄰兩次枚舉的時候選取的區(qū)間是有相交的部分的。由此可以考慮使用尺取法來維護(hù)所選的區(qū)間衣赶。
于是我們需要復(fù)制兩個原來的區(qū)間序列诊赊,分別以按升序和按升序來排序(分別記為數(shù)列和數(shù)列)。那么每次我們枚舉完第個數(shù)之后府瞄,我們便需要根據(jù)去掉所有右值小于的區(qū)間碧磅,再根據(jù)加入所有左值等于開頭的區(qū)間。這樣我們就可以從所有只覆蓋到的區(qū)間推出所有只覆蓋的區(qū)間遵馆。
使用尺取法之后的總時間復(fù)雜度為续崖。不知道會不會算錯不過如果按這個算法的話貌似比標(biāo)程要優(yōu)一點(diǎn)(標(biāo)程是)?感覺十分的慌啊總覺得要么就是時間復(fù)雜度算錯了要么就是有可以叉掉我的case团搞。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int INF = 1e8;
struct SegTree
{
int a[400005];
int b[400005];
int lazy[400005];
void init()
{
memset(lazy, 0, sizeof(lazy));
for(int i = 0; i < 400005; i++)
{
a[i] = INF;
b[i] = -1 * INF;
}
}
void SetElem(int pos, int elem, int l, int r, int p)
{
if(l == r)
{
a[p] = elem;
b[p] = elem;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
{
SetElem(pos, elem, l, mid, p << 1);
}
else
{
SetElem(pos, elem, mid + 1, r, (p << 1) + 1);
}
a[p] = min(a[p << 1], a[(p << 1) + 1]);
b[p] = max(b[p << 1], b[(p << 1) + 1]);
}
void SetSeg(int pl, int pr, int elem, int l, int r, int p)
{
if((pl == l) && (pr == r))
{
lazy[p] += elem;
return;
}
int mid = (l + r) >> 1;
if(pr <= mid)
{
SetSeg(pl, pr, elem, l, mid, p << 1);
}
else if(pl > mid)
{
SetSeg(pl, pr, elem, mid + 1, r, (p << 1) + 1);
}
else
{
SetSeg(pl, mid, elem, l, mid, p << 1);
SetSeg(mid + 1, pr, elem, mid + 1, r, (p << 1) + 1);
}
a[p] = min(a[p << 1] + lazy[p << 1], a[(p << 1) + 1] + lazy[(p << 1) + 1]);
b[p] = max(b[p << 1] + lazy[p << 1], b[(p << 1) + 1] + lazy[(p << 1) + 1]);
}
int GetMin()
{
return a[1] + lazy[1];
}
int GetMax()
{
return b[1] + lazy[1];
}
int GetMinPos(int l, int r, int p)
{
if(l == r)
{
return l;
}
int mid = (l + r) >> 1;
if(a[p] + lazy[p] == a[p << 1] + lazy[p << 1])
{
return GetMinPos(l, mid, p << 1);
}
else
{
return GetMinPos(mid + 1, r, (p << 1) + 1);
}
}
}S;
struct node
{
int l, r;
int pos;
node(int _l = 0, int _r = 0, int _pos = 0)
{
l = _l;
r = _r;
pos = _pos;
}
};
bool cmp_l(const node &a, const node &b)
{
if(a.l != b.l)
{
return a.l < b.l;
}
return a.r < b.r;
}
bool cmp_r(const node &a, const node &b)
{
if(a.r != b.r)
{
return a.r < b.r;
}
return a.l < b.l;
}
vector<node> vl, vr;
int main()
{
int n, m;
while(~scanf("%d%d", &n, &m))
{
S.init();
vl.clear();
vr.clear();
for(int i = 1; i <= n; i++)
{
int a;
scanf("%d", &a);
S.SetElem(i, a, 1, n, 1);
}
for(int i = 0; i < m; i++)
{
int l, r;
scanf("%d%d", &l, &r);
vl.push_back(node(l, r, i + 1));
vr.push_back(node(l, r, i + 1));
}
sort(vl.begin(), vl.end(), cmp_l);
sort(vr.begin(), vr.end(), cmp_r);
int pos_l = 0;
int pos_r = 0;
int ans = -1;
int ans_pos;
for(int i = 1; i <= n; i++)
{
while((pos_r < m) && (vr[pos_r].r < i))
{
S.SetSeg(vr[pos_r].l, vr[pos_r].r, 1, 1, n, 1);
pos_r++;
}
while((pos_l < m) && (vl[pos_l].l == i))
{
S.SetSeg(vl[pos_l].l, vl[pos_l].r, -1, 1, n, 1);
pos_l++;
}
if(ans < S.GetMax() - S.GetMin())
{
ans = S.GetMax() - S.GetMin();
ans_pos = i;
}
}
while(pos_l < m)
{
S.SetSeg(vl[pos_l].l, vl[pos_l].r, -1, 1, n, 1);
pos_l++;
}
while(pos_r < m)
{
S.SetSeg(vr[pos_r].l, vr[pos_r].r, 1, 1, n, 1);
pos_r++;
}
vector<int> ans_v;
for(int i = 0; i < m; i++)
{
if((ans_pos <= vl[i].r) && (ans_pos >= vl[i].l))
{
ans_v.push_back(vl[i].pos);
}
}
printf("%d\n%d\n", ans, (int)ans_v.size());
for(int i = 0; i < ans_v.size(); i++)
{
if(i)printf(" ");
printf("%d", ans_v[i]);
}
printf("\n");
}
return 0;
}
Problem F
一個十分顯然的想法是严望,對圖建最小生成樹。然后對于剩下的邊而言逻恐,只要查詢最小生成樹上從到的路徑的最大值是否等于即可像吻。如果是則意味著當(dāng)前最小生成樹中從到的路徑上有一條邊的權(quán)值跟當(dāng)前邊相等峻黍,即表明可以用邊替換之使其依舊為最小生成樹。那么這顯然是需要加權(quán)值的邊拨匆,于是答案+1姆涩。
對于上述查詢的話可以使用樹鏈剖分維護(hù)最小生成樹來實(shí)現(xiàn),總時間復(fù)雜度為惭每。但是事實(shí)上有更加簡單且高效的解法骨饿。
我們可以把邊按權(quán)值歸類,并讓這些分類按權(quán)值從小到大排序台腥。那么對于每一類邊而言宏赘,其權(quán)值就是相等的。那么我們便可以按權(quán)值從小到大枚舉每一類邊黎侈。對于每一類邊而言察署,用這些邊做Kruskal的過程。如果出現(xiàn)會構(gòu)成環(huán)且不是自環(huán)的邊峻汉,那么表明這條邊肯定跟同一類邊沖突了贴汪,那么答案加一。然后再每次對一類邊做完Kruskal之后休吠,按照當(dāng)前Kruskal的并查集對當(dāng)前圖縮點(diǎn)扳埂。
思路很簡潔,但是縮點(diǎn)這個過程時耗很長(如果每次都去縮點(diǎn)的話瘤礁,時間復(fù)雜度是的)聂喇。所以需要考慮如何對縮點(diǎn)這個過程進(jìn)行優(yōu)化。
于是考慮不直接縮點(diǎn)蔚携,而是將上一個類Kruskal之后的并查集存下來用于當(dāng)前類Kruskal過程的查詢希太。于是就可以這么做:維護(hù)兩個并查集D1和D2,其中D1記錄的就是上一個類遍歷完之后的并查集酝蜒。然后從小到大遍歷每一類邊誊辉。對于每一類邊而言做兩遍Kruskal:
- 第一遍針對D2,如果出現(xiàn)無法插入D2的邊亡脑,則先去D1檢查一下兩個點(diǎn)是否已經(jīng)并起來了堕澄。如果已經(jīng)并起來了的話說明當(dāng)前邊在現(xiàn)在這個縮完點(diǎn)的圖中是一條自環(huán)邊,直接舍棄霉咨。否則則表明該邊是一條沖突邊蛙紫,答案加一
- 第二遍針對D1,加就完事了途戒,僅僅是用于將D2的并查集同步到D1坑傅。
分類使用map
維護(hù),總的時間復(fù)雜度為喷斋。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
const int MAXN = 200005;
struct DSU
{
int prt[MAXN];
int n;
void init(int _n)
{
n = _n;
for(int i = 1; i <= n; i++)
{
prt[i] = i;
}
}
int find(int x)
{
if(x == prt[x])return x;
return prt[x] = find(prt[x]);
}
bool unite(int x, int y)
{
x = find(x);
y = find(y);
if(x == y)return false;
if(x > y)swap(x, y);
prt[y] = x;
return true;
}
};
struct Kruskal
{
map< int, vector< pair<int, int> > > mp;
int n;
DSU D1, D2;
void init(int _n)
{
n = _n;
mp.clear();
D1.init(n);
D2.init(n);
}
void push_back(int _u, int _v, int _w)
{
mp[_w].push_back(make_pair(_u, _v));
}
int Solve()
{
int ret = 0;
for(auto it = mp.begin(); it != mp.end(); it++)
{
vector< pair<int, int> > &v = it->second;
for(int i = 0; i < v.size(); i++)
{
if(!D2.unite(v[i].first, v[i].second))
{
if(D1.find(v[i].first) == D1.find(v[i].second))
{
continue;
}
ret++;
}
}
for(int i = 0; i < v.size(); i++)
{
D1.unite(v[i].first, v[i].second);
}
}
return ret;
}
}K;
int main()
{
int n, m;
while(~scanf("%d%d", &n, &m))
{
K.init(n);
for(int i = 0; i < m; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
K.push_back(u, v, w);
}
printf("%d\n", K.Solve());
}
}