爷回来啦!
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;
}