爺回來啦!
2更
F要用fft或ntt,慌了
E. Voting (Easy/Hard Version) (2s 256Mb)
題目大意
你需要讓(2e5)個人全部給你投票。對於每個人,要麼花的錢讓他爲你投票,要麼如果已經有個人爲你投票了,他會免費爲你投票,也就是說投票的過程是有漸進的。求讓全部爲你投票的最小花費。
分析
將m相等的人歸爲一類,並按照m值從大到小考慮(降序很重要)。
簡單證明:對於最大m,假如將剩下的m比它小的都買了也不夠最大m的話,必須從這最大m裏買一些人,否則可以縮小問題範圍爲除去是最大m的人的問題。既然要買不如先買,然後也可以縮小問題範圍。之後第二大m,第三大m。。。
注意之前的縮小後去除的人也可以買,計算夠不夠當前m時也要將這些提前買的m加上。
被題解帶着用了multiset,其實也可以用優先隊列的。
代碼
int main()
{
int T = read();
while(T--)
{
n = read();
for(int i = 1; i <= n; i++)
{
a[i].m = read(), a[i].p = read();
}
sort(a+1, a+n+1, cmp);
int l = 1, r, cnt = 0;
long long ans = 0;
while(l <= n)
{
r = l; while(r < n && a[r+1].m == a[l].m) r++;
for(int i = l; i <= r; i++) s.insert(a[i]);
if(n-r+cnt < a[l].m)
{
int cha = a[l].m - (n-r+cnt);
cnt += cha;
while(cha--)
{
ans += (long long) (*s.begin()).p;
s.erase(s.begin());
}
}
l = r+1;
}
printf("%I64d\n", ans);
if(T) s.erase(s.begin(), s.end());
}
return 0;
}
D. Salary Changing (3s 256Mb)
題目大意
給n(2e5且爲奇數)個人發工資,一共有s(2e14)塊錢。每個人給的錢必須在 (1e9)之間。使給的n個錢數中位數最大,求中位數最大可以是多少。
分析
一開始考慮了二分但是覺得不滿足全域的單調性。實際上考慮了L和R的初值以後就滿足。
check部分,對於每個中位數,分三種人。
- ,這種人只能作爲中位數前面,給他最少的 即可。
- ,這種人只能作爲中位數後面,給他最少的 。
- ,這種人既可以做前面也可以做後面,每個人要麼給 要麼給 ,根據 排序即可。
最後錢不夠用或者前兩種人每種太多都不行。
代碼
const int maxn = 2e5+10;
pair<ll, ll> a[maxn];
ll s;
int n, vis[maxn];
int check(long long mid)
{
ll tmp = 0;
int c1 = 0, c2 = 0;
for(int i = 1; i <= n; i++) vis[i] = 0;
for(int i = 1; i <= n; i++) if(a[i].second < mid)
{
vis[i] = 1; tmp += a[i].first; c1++;
}
//if(c1 > n/2) return 0;
for(int i = 1; i <= n; i++) if(!vis[i] && a[i].first >= mid)
{
vis[i] = 1; tmp += a[i].first; c2++;
}
for(int i = 1; i <= n && c1 != n/2; i++) if(!vis[i])
{
vis[i] = 1; tmp += a[i].first; c1++;
}
for(int i = 1; i <= n; i++) if(!vis[i])
{
tmp += mid; c2++;
}
return tmp <= s && c1 == n/2 && c2 == n/2+1;
}
int main()
{
int T = read();
while(T--)
{
n = read(); s = read();
for(int i = 1, u, v; i <= n; i++)
{
u = read(), v = read();
a[i] = {u, v};
}
sort(a+1, a+n+1);
ll l = a[n/2+1].first, r = s;
while(l != r)
{
ll mid = l + ((r-l)>>1) + 1;
if(check(mid)) l = mid;
else r = mid-1;
}
printf("%I64d\n", l);
}
return 0;
}
C. Minimize The Integer (2s 256Mb)
題目大意
給你n(3e5)長0-9串,如果相鄰digit一奇數一偶數則可以交換位置(同爲奇數或同爲偶數不行)。求可以得到的字典序最小串。
分析
分析性質。由題意,奇數字串和偶數字串各自順序不會變(必要性),滿足這一性質的串可以任意構造(充分性),隨便貪(歸併)。
代碼
const int maxn = 3e5+10;
int n, a[maxn], c[maxn], d[maxn]; char s[maxn];
int main()
{
int T = read();
while(T--)
{
scanf("%s", s+1);
int n = strlen(s+1);
for(int i = 1; i <= n; i++) a[i] = s[i]-'0';
int tot1 = 0, tot2 = 0;
for(int i = 1; i <= n; i++)
{
if(a[i]&1) c[++tot1] = a[i];
else d[++tot2] = a[i];
}
int x = 1, y = 1;
while(x <= tot1 && y <= tot2)
{
if(c[x] < d[y]) putchar(c[x++]+'0');
else putchar(d[y++]+'0');
}
while(x <= tot1) putchar(c[x++]+'0');
while(y <= tot2) putchar(d[y++]+'0');
putchar(10);
}
return 0;
}
B. Binary Palindromes (2s 256Mb)
題目大意
給你n(50)個01串,,可以交換兩個bit的位置任意次(可以不同串),問最多能構成多少迴文串。
分析
有點意思。一開始想複雜了。任意交換就可以看0和1總個數,但每個串長度固定。先把0和1都拿出來,分給每個串。
對於奇數長串,該串無論0和1有多少都能安排成迴文(0和1的個數必然一奇數一偶數)。對於偶數長串,如果0和1的個數是偶數、偶數,可以安排成迴文。如果奇數、奇數,則不可以(違反必要性)。
考慮一下的話答案要麼是n要麼是n-1。
對於偶,不考慮,因爲使用了0和1後,對0和1總數奇偶性無影響。對於奇數串,不考慮最中間位置就成偶數串(最中間填啥都行)。
- 如果0和1個數全是偶數,隨便填。
- 如果奇數、偶數,至少一個奇數串。拿一個0或1出來放在奇數串最中間,去構成偶數、偶數,隨便填。
- 如果奇數,奇數,拿一個0、一個1,去構成偶數、偶數。如果奇數串大於等於2,拿出來的可以放在最中間處理;否則只能廢掉一個串。
所以只有一種情況答案是n-1。
if(b[0]&1 == 1 && b[1]&1 == 1 && tmp < 2) ans = n-1;
//0,1個數都是奇數,且沒有奇數串(tmp爲偶)
代碼
const int maxn = 60;
int n, a[maxn];
int main()
{
int T = read();
while(T--)
{
n = read();
int b[2] = {0, 0}, tmp = 0;
for(int i = 1; i <= n; i++)
{
char c; int tot = 0;
while(c = getchar())
{
if(c == '\n') break;
tot++;
b[c-'0']++;
}
tmp += tot&1;
}
int ans;
if(b[0]&1 == 1 && b[1]&1 == 1 && tmp < 2) ans = n-1;
else ans = n;
printf("%d\n", ans);
}
return 0;
}
A. Broken Keyboard (1s 256Mb)
題目大意
一個壞的打字機,某些字母按鍵按一次輸出兩個。給你一個輸出文本,判斷哪些按鍵一定是好的。
分析
一開始被帶進去了,實際上壞的鍵只能按出偶數個連續的相同字母,好的可奇可偶。所以有奇數個連續的一定是好的。
代碼
const int maxn = 1e3+10;
char s[maxn];
int vis[30];
int main()
{
int T = read();
while(T--)
{
memset(vis, 0, sizeof(vis));
scanf("%s", s+1);
int len = strlen(s+1);
int x = 1;
while(x <= len)
{
int y = x;
while(y <= len && s[y] == s[x]) y++;
if((y-x)&1) vis[s[x]-'a'+1] = 1;
x = y;
}
for(int i = 1; i <= 26; i++) if(vis[i]) putchar('a'+i-1);
putchar(10);
}
return 0;
}