补题--8 (Round 75 (Rated for Div. 2))

比赛网址:https://vjudge.net/contest/381170
codeforce的网址:https://codeforces.com/contest/1251

A( Rated for Div. 2 E1)看G

B( Rated for Div. 2 C)

题意:
可以交换奇偶性不同的相邻的数,然后获得最小的数。
思路:
我的思路:
找到奇偶性不同的数然后进行交换,然后与之前的数进行比较,然后循环进行,知道找到最小的那个最后输出,我感觉应该运用到了记忆数组的那一部分的知识。
官方思路:
让我们考虑两个数字序列:e1,e2,…,ek和o1,o2,…,om,在那里e1中的第一个偶数a, e2是第二个偶数,以此类推o1中的第一个奇数a, o2是第二个奇数,以此类推。
由于不能交换相同奇偶的数字,所以序列e的偶数位数a从未改变过。序列o的奇数a也从未改变过。所以答案中的第一个数字等于e1或去o1。由于我们必须将答案最小化,所以我们必须选择min(e1,o1)作为答案中的第一个数字,他们将其从相应的序列中删除(以这种方式)e转成e2,e3,…,ek或序列o转成o2,o3,…,om)。第二,第三位和下面的数字需要以同样的方式选择。
官方代码:

#include <bits/stdc++.h>
using namespace std;
int t;
string a;

int main() {	
	cin >> t;
	for(int tc = 0; tc < t; ++tc){
		cin >> a;
		string s[2];
		for(auto x : a)
			s[int(x - '0') & 1] += x;
		reverse(s[0].begin(), s[0].end());
		reverse(s[1].begin(), s[1].end());
		string res = "";
		while(!(s[0].empty() && s[1].empty())){
			if(s[0].empty()){
				res += s[1].back();
				s[1].pop_back();
				continue;
			}
			if(s[1].empty()){
				res += s[0].back();
				s[0].pop_back();
				continue;
			}

			if(s[0].back() < s[1].back()){
				res += s[0].back();
				s[0].pop_back();
			}
			else{
				res += s[1].back();
				s[1].pop_back();
			}
		}

		cout << res << endl;
	}	
	return 0;
} 

C( Rated for Div. 2 A)

题意:
一个字符串,输入的时候如果出现偶数个则是坏了,如果是奇数个就是正常,然后找出坏的。
思路:
就是找到相同连续的导出就可以了

#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
int n,m,maxx,ans,aans,flag,a[30];
string s;
int main()
{
    cin.tie(0);
    ios::sync_with_stdio(0);
    int t;
    cin>>t;
   while(t--)
    {
        memset(a,0,sizeof(a));
        cin>>s;
        n=s.size();
        for(int i=0;i<n;i++)
        {
            if(i==n-1||(i!=n-1&&s[i]!=s[i+1])) a[s[i]-'a']=1;
            else if(i!=n-1&&s[i]==s[i+1]) i++;
        }
        for(int i=0;i<=26;i++)
        {
            if(a[i]) cout<<char(i+'a');
        }
        cout<<endl;
    }
    return 0;
}

D( Rated for Div. 2 D)

题意:
n 有人为你工作,你必须把工资分配给你的雇员。一开始,你s给它的钱,还有i-该雇员应从li到ri美元。你必须以这样一种方式分配工资,即工资中位数是最大可能.要找到奇数长度序列的中间值,必须对其进行排序,并在排序后取中间位置的元素。找到可以获得的最大中位数薪资。
思路:
我的思路:
找到中间的,然后和前后进行比较,应该是要运用到二进制的知识点。
官网思路:
放任f(mid)至少要有相等的最低金额才能获得工资的中位数。mid。我们将通过二进制搜索来解决这个问题mid.
假设必须计算获得中位数工资的最低金额。mid。让我们把所有的工资分成三组:
ri<mid;
mid≤li;
li<mid≤ri.
为了达到工资的中位数mid至少一定有n+12高于或等于mid。让我们表示一下cnt.
注意,第一组的薪资不能增加cnt因此,为这个群体支付最低工资对我们是有益的。如果第二组总是增加cnt所以支付最低工资对我们也是有益的。
第三组的薪水更有趣。按每一薪金计算[li,ri]在这组中我们可以支付mid增量cnt或者我们可以li不要增加cnt。价值cnt应该增加rem=max(0,n+12−cnt)。所以,如果第三组的大小小于rem我们无法获得工资中位数mid。否则,我们可以定义我们可以拿多少工资值。li选择了最小的。

#include <bits/stdc++.h>

using namespace std;

const int N = int(2e5) + 99;
const int INF = int(1e9) + 100;

int t;
int n;
long long s;
pair<int, int> p[N];

bool ok(int mid){
	long long sum = 0;
	int cnt = 0;
	vector <int> v;
	for(int i = 0; i < n; ++i){
		if(p[i].second < mid)
			sum += p[i].first;
		else if(p[i].first >= mid){
			sum += p[i].first;
			++cnt;
		}
		else
			v.push_back(p[i].first);
	}

	assert(is_sorted(v.begin(), v.end()));
	
	int need = max(0, (n + 1) / 2 - cnt);
	if(need > v.size()) return false;
	for(int i = 0; i < v.size(); ++i){
		if(i < v.size() - need)
			sum += v[i];
		else
			sum += mid;
	}

	return sum <= s;
}

int main() {	
	scanf("%d", &t);
	for(int tc = 0; tc < t; ++tc){
		scanf("%d %lld", &n, &s);
		for(int i = 0; i < n; ++i)
			scanf("%d %d", &p[i].first, &p[i].second);
		
		sort(p, p + n);
		int lf = 1, rg = INF; ///WA -> 10^9
		while(rg - lf > 1){
			int mid = (lf + rg) / 2;
			if(ok(mid)) lf = mid;
			else rg = mid;
		}
		
		printf("%d\n", lf);
	}
	return 0;
}                

E( Rated for Div. 2 F)

题意:

#include<bits/stdc++.h>

using namespace std;

const int LOGN = 20;
const int N = (1 << LOGN);
const int MOD = 998244353;
const int g = 3;

#define forn(i, n) for(int i = 0; i < int(n); i++)

inline int mul(int a, int b)
{
	return (a * 1ll * b) % MOD;
}

inline int norm(int a) 
{
	while(a >= MOD)
		a -= MOD;
	while(a < 0)
		a += MOD;
	return a;
}

inline int binPow(int a, int k) 
{
	int ans = 1;
	while(k > 0) 
	{
		if(k & 1)
			ans = mul(ans, a);
		a = mul(a, a);
		k >>= 1;
	}
	return ans;
}

inline int inv(int a) 
{
	return binPow(a, MOD - 2);
}

vector<int> w[LOGN];
vector<int> iw[LOGN];
vector<int> rv[LOGN];

void precalc() 
{
	int wb = binPow(g, (MOD - 1) / (1 << LOGN));
	
	for(int st = 0; st < LOGN; st++) 
	{
		w[st].assign(1 << st, 1);
		iw[st].assign(1 << st, 1);
		
		int bw = binPow(wb, 1 << (LOGN - st - 1));
		int ibw = inv(bw);
		
		int cw = 1;
		int icw = 1;
		
		for(int k = 0; k < (1 << st); k++) 
		{
			w[st][k] = cw;
			iw[st][k] = icw;
			
			cw = mul(cw, bw);
			icw = mul(icw, ibw);
		}
		
		rv[st].assign(1 << st, 0);
		
		if(st == 0) 
		{
			rv[st][0] = 0;
			continue;
		}
		int h = (1 << (st - 1));
		for(int k = 0; k < (1 << st); k++)
			rv[st][k] = (rv[st - 1][k & (h - 1)] << 1) | (k >= h);
	}
}

inline void fft(int a[N], int n, int ln, bool inverse) 
{	
	for(int i = 0; i < n; i++) 
	{
		int ni = rv[ln][i];
		if(i < ni)
			swap(a[i], a[ni]);
	}
	
	for(int st = 0; (1 << st) < n; st++) 
	{
		int len = (1 << st);
		for(int k = 0; k < n; k += (len << 1)) 
		{
			for(int pos = k; pos < k + len; pos++) 
			{
				int l = a[pos];
				int r = mul(a[pos + len], (inverse ? iw[st][pos - k] : w[st][pos - k]));
				
				a[pos] = norm(l + r);
				a[pos + len] = norm(l - r);
			}
		}
	}
	
	if(inverse) 
	{
		int in = inv(n);
		for(int i = 0; i < n; i++)
			a[i] = mul(a[i], in);
	}
}

int aa[N], bb[N], cc[N];

inline void multiply(int a[N], int sza, int b[N], int szb, int c[N], int &szc) 
{
	int n = 1, ln = 0;
	while(n < (sza + szb))
		n <<= 1, ln++;
	for(int i = 0; i < n; i++)
		aa[i] = (i < sza ? a[i] : 0);
	for(int i = 0; i < n; i++)
		bb[i] = (i < szb ? b[i] : 0);
		
	fft(aa, n, ln, false);
	fft(bb, n, ln, false);
	
	for(int i = 0; i < n; i++)
		cc[i] = mul(aa[i], bb[i]);
		
	fft(cc, n, ln, true);
	
	szc = n;
	for(int i = 0; i < n; i++)
		c[i] = cc[i];
}

vector<int> T[N];

int a[N];
int b[N];
int n, k;
int ans[N];
int Q[N];
int fact[N];
int rfact[N];

int auxa[N];
int auxb[N];
int auxc[N];

int C(int n, int k)
{
	if(n < 0 || k < 0 || k > n) return 0;
	return mul(fact[n], mul(rfact[k], rfact[n - k]));
}

vector<int> newtonExp(int a, int b, int p)
{
	vector<int> res(p + 1);
	for(int i = 0; i <= p; i++)
		res[i] = mul(C(p, i), mul(binPow(a, i), binPow(b, p - i)));
	return res;
}

int main()
{
	precalc();
	fact[0] = 1;
	for(int i = 1; i < N; i++) fact[i] = mul(fact[i - 1], i);
	for(int i = 0; i < N; i++) rfact[i] = inv(fact[i]);
	
	scanf("%d %d", &n, &k);
	for(int i = 0; i < n; i++) scanf("%d", &a[i]);
	for(int i = 0; i < k; i++) scanf("%d", &b[i]);
	int q;
	scanf("%d", &q);
	for(int i = 0; i < q; i++) scanf("%d", &Q[i]);
	
	map<int, int> cnt;
	for(int i = 0; i < n; i++)
		cnt[a[i]]++;
	
	for(int i = 0; i < k; i++)
	{
		int redL = b[i];
		int cnt1 = 0;
		int cnt2 = 0;
		for(auto x : cnt)
		{
			if(x.first >= redL) 
				break;
			if(x.second == 1)
				cnt1++;
			else
				cnt2++;
		}
		memset(auxa, 0, sizeof auxa);
		memset(auxb, 0, sizeof auxb);
		memset(auxc, 0, sizeof auxc);
		vector<int> p1 = newtonExp(2, 1, cnt1);
		vector<int> p2 = newtonExp(1, 1, cnt2 * 2);
		int sa = p1.size();
		int sb = p2.size();
		int sc;
		for(int j = 0; j < sa; j++)
			auxa[j] = p1[j];
		for(int j = 0; j < sb; j++)
			auxb[j] = p2[j];
		multiply(auxa, sa, auxb, sb, auxc, sc);
		for(int j = 0; j < q; j++)
		{
			int cntW = Q[j] / 2 - redL - 1;
			if(cntW >= 0 && cntW < sc)
				ans[j] = norm(ans[j] + auxc[cntW]);
		}
	}
	
	for(int i = 0; i < q; i++)
		printf("%d\n", ans[i]);
}

F( Rated for Div. 2 B)

题意:
给你n个二进制字符串,然后你可以随意组合他们,
使他们形成原来的字符串相同长度的回文数组。
然后计算出最多可以形成几个回文数组。
思路:
我比赛时的思路:应该是先将最短的那个序列改为回文,然后再按长度一次进行改变。但是事实验证我这个想法漏洞不对。
大佬的思路:
首先字符串的长度是奇数的时候都是回文的状态,然后是偶数的时候如果要改为回文,那么0,1的个数一定是偶数个。那如果有至少一个奇数的时候,需要用这个奇数给其他的数进行修改。然后就可以所有的都改为回文,但是如果没有奇数的时候。
对于不能构成回文串的情况只有一种(奇数个1,奇数个0).
对于长度为奇数的字符串,一定能构成。因为奇数=偶数+奇数。所以这个要特判。
所以最终我们要找长度不是奇数且有奇数个1,奇数个0的情况就行。
代码网址:https://blog.youkuaiyun.com/qq_43690454/article/details/104105663

#include<bits/stdc++.h>
using namespace std;
int  t,n;
char s[1000005];
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		int l=0,r=0,f=0;
		for(int i=1;i<=n;i++)
		{
			cin>>s;
			int m=strlen(s);
			if(m&1) f=1;
			for(int j=0;j<m;j++)
			{
				if(s[j]=='1') l++;
				else r++;
			}
		}
		if((l&1)&&(r&1)&&!f) n--;
		cout<<n<<endl;
	}
}

G( Rated for Div. 2 E2)

题意:
就是有n个人投票,然后对于第i个人如果有其它mi个人投票给你,那么他也会投票给你,不然你就得给他pi的钱才投给你。求怎样花钱少,输出钱数。

#include <bits/stdc++.h>
using namespace std;
const int N = int(3e5) + 99;
int t, n;
vector <int> v[N];
int main() {	
	int t;
	scanf("%d", &t);
	for(int tc = 0; tc < t; ++tc){
		scanf("%d", &n);
		for(int i = 0; i < n; ++i)
			v[i].clear();
		for(int i = 0; i < n; ++i){
			int x, s;
			scanf("%d %d", &x, &s);
			v[x].push_back(s);
		}		
		
		multiset <int > q;
		long long res = 0;
		int pref = n;
		int cnt = 0;
		for(int i = n - 1; i >= 0; --i){
			pref -= v[i].size();
			int need = i - pref;
			for(auto x : v[i]) q.insert(x);

			while(cnt < need){
				++cnt;
				res += *q.begin();				
				q.erase(q.begin());
			}
		}		

        	printf("%lld\n", res);
        }
	return 0;
}                       

小总结:

我发现我的思路真的是很怪,然后对于不同的题目都有自己的看法,但是我的看法总是在代码的书写上搞不出来,但是感觉自己的思路也是可以的,但最后还是和大多数人不一样,应该还是自己的问题面对问题的思考方式要改变一下吧。其实我感觉应该是自己代码写得不好的问题,题目相同,思路每个人不同,应该是提高自己写代码的实力,把自己的代码搞出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值