【NOIP2018模拟赛2018.10.31】

在这里插入图片描述在这里插入图片描述

简单模拟

 贪心思想,每次尽量输出最大的数,维护一个后缀最大值,表示当前位置(包括当前数)到n中的最大值,可以通过倒推得到。
 首先栈中压入第一个数,然后进行比较:若当前位置后缀最大值小于栈顶元素,则弹出栈顶,否则依次压入栈中直至后面没有比当前栈顶大的数,如此重复即可。
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define gc getchar
#define pt putchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 2e5 + 5;
int n,maxnow;
int a[MAXN];
int sta[MAXN],top = 0;
int ans[MAXN],len = 0;
int rep[MAXN];
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}


int main()
{
	in(n); 
	for(int i = 1;i <= n;i++) in(a[i]);
	rep[n] = a[n];
	for(int i = n-1;i >= 1;i--) 
		rep[i] = max(rep[i+1],a[i]);
	sta[++top] = a[1]; int i = 2;
	while(i <= n)
	{
		if(sta[top] < rep[i])
		{
			while(sta[top] < rep[i]) sta[++top] = a[i++]; 
			ans[++len] = sta[top--];
		}
		else
			if(top) ans[++len] = sta[top--];
	}
	while(top) ans[++len] = sta[top--];
	for(int i = 1;i < len;i++) out(ans[i]),ko; out(ans[len]);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

贪心

可以看到1~7点 n <= 10,也就是爆搜70分的意思。。
 正解贪心,可以看到攻击力最大30000,于是可以开一个桶,桶排序,记录每个攻击的出现次数,顺便处理当前攻击力需要几个腐败药剂,用了后剩下的是1~3的哪一个,拉过来所需法力值。(具体看代码)
 枚举药剂使用次数,从0开始,然后两个指针pre,now进行桶的遍历(每次只要算多使用一个药剂多出来的那一段就好),不断累加当前所用魔法值,拉过来小弟中1,2,3分别攻击力的数量(后面贪心要用),不断累加攻击力直到大于m,break。
 然后贪心处理溢出来那一部分。可以想到先一个一个删剩余攻击力为3的小弟(毕竟法力值消耗为4),删到其没有了或是小于等于m就停止。(删多了要加回去)。然后优先删剩余攻击力为1的小弟(相比攻击力为2的要劣一些),若还大于m就再删攻击力为2的小弟,直到所有小弟都没了或是小于等于m了,此时答案一定最优。
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define gc getchar
#define pt putchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 3e4 + 5;
const int INF = 999999999;
int n,m,maxTimes = 0;
int tong[MAXN],Times[MAXN];
int res[MAXN],need[MAXN];
ll one,two,three;
bool vis[MAXN];
ll sum = 0,final = 0;
ll ans = 0,mofa = 0;
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

int main()
{
	in(n),in(m);
	for(int i = 1;i <= n;i++){
		int x; in(x); 
		if(x <= 0) continue;
		tong[x]++;
		if(!need[x])
		{
			Times[x] = x/3; int tmp = x % 3;
			res[x] = tmp == 0 ? 3 : tmp;
			need[x] = tmp == 0 ? 4 : 1;
			if(tmp == 0) Times[x]--;
		}
		maxTimes = max(maxTimes,Times[x]); 
	}
	ll pre = 1,sum = 0;
	for(int i = 0;i <= maxTimes;i++){
		ll now = (ll)i*3 + 3;
		for(int j = pre;j <= now;j++)
		{
			if(tong[j])
			{
				if(res[j] == 1) one += 1ll * tong[j];
				else if(res[j] == 2) two += 1ll * tong[j];
				else three += 1ll * tong[j];
				sum += 1ll * tong[j] * res[j];
				mofa += tong[j]*need[j];
			} 
			if(sum >= m) {ans = i; mofa += i; goto next;}
		}
		pre = now + 1;
	}
next:
	if(sum < m) {out(-1); return 0;}
	while(sum > m && three) sum -= 3,mofa -= 4,three--;
	if(sum == m) {out(ans),ko,out(mofa); return 0;}
	else if(sum < m) sum += 3,mofa += 4;
	while(sum > m && one) sum -= 1,mofa -= 1,one--;
	while(sum > m && two) sum -= 2,mofa -= 1,two--;
	if(sum < m) mofa += 1;
	out(ans),ko,out(mofa);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

贪心+二分

 首先题目中那个什么只能拐一次弯的那种明显不怀好意的说法,其实意思就是分成的两个部分不能是凹形的,代码实现中意思就是我扩张每一行时必须与上一行持平或是更加往外面伸。
 于是考虑贪心,先找到全局最大值与最小值,假设最大值在一边,最小值在另一边,二分答案,检验合理性:Max - Map[i][j] <= x,另一边Map[i][j] - Min > x。
 扩展时满足条件的前提下尽可能扩展,(可以想到有最大值这面满足条件下扩展的越大,另一面需要满足条件的点就越少,我们就越有可能使答案合理)检验合理性,合理便二分减少当前答案,否则增加。
 由于不凹,两块的分界一定是斜着的而且有四种情况,我们可以用一个二分而每次将图进行旋转(当然你也可以写4个二分)即可。
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define gc getchar
#define pt putchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 2000 + 5;
const int INF = 999999999;
int n,m,Max,Min,ans = INF;
int Map[MAXN][MAXN];
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

void move()
{
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= (m>>1);j++)
			swap(Map[i][j],Map[i][m-j+1]);
}

bool check(int x)
{
	int pre = m;
	for(int i = 1;i <= n;i++)
	{
		int pos = 0;
		for(int j = 1;j <= min(pre,m);j++)
			if(Max - Map[i][j] <= x) pos = max(pos,j);
			else break;
		pre = pos;
		for(int j = pos+1;j <= m;j++)
			if(Map[i][j] - Min > x) return 0;
	}
	return 1;
}

void erfen()
{
	int l = 0,r = Max - Min + 1;
	while(l < r)
	{
		int mid = (l + r) >> 1;
		if(check(mid)) ans = min(ans,mid),r = mid;
		else l = mid+1;
	}
}

void move1()
{
	for(int i = 1;i <= (n>>1);i++)
		for(int j = 1;j <= m;j++)
			swap(Map[i][j],Map[n-i+1][j]);
}

int get_ans()
{
	for(int i = 1;i <= 4;i++) 
	{
		erfen();
		if(i == 2) move1();
		else if(i != 4) move();
	}
	return ans;
}

int main()
{
	in(n); in(m);
	Max = -INF,Min = INF;
	for(int i = 1;i <= n;i++) 
		for(int j = 1;j <= m;j++)
		{
			in(Map[i][j]);
			Max = max(Max,Map[i][j]);
			Min = min(Min,Map[i][j]);
		}
//	move();
	out(get_ans());
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数学题

推出公式,带入计算即可。当然本蒟蒻是做不到的数学题打的全是暴力蛤蛤蛤
题解感性地理解一下直接代公式+算逆序对就行啦各位
在这里插入图片描述
有一点点写错了,那个(n-2i+1)*vj/maxv 应该是(n-2i+1)*vi/maxv,当时看题解时感觉一脸懵,就是那个j应该是i,应该就可以算了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define gc getchar
#define pt putchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 1e5 + 5;
int T,C;
int n,A;
ll cnt[MAXN];
ll len,v[MAXN],t[MAXN],m[MAXN];
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

int lowbit(int x) {return x & (-x);}

void updata(int x)
{
	while(x <= n)
	{
		cnt[x]++;
		x += lowbit(x);		
	}
}

int ask(int x)
{
	int res = 0;
	while(x) 
	{
		res += cnt[x];
		x -= lowbit(x);
	}
	return res;
}

int main()
{
//	freopen(".in","r",stdin); 
//	freopen(".out","w",stdout); 
	in(T),in(C);
	while(T--)
	{
		in(n); in(A); lin(len); ll ans = 0,vmax = 0;
		for(int i = 1;i <= n;i++) 
		{
			cnt[i] = 0;
			lin(v[i]); vmax = max(v[i],vmax);
			v[i] *= len;
		}
		if(n == 1) {out(0); ex;continue;}
		sort(v+1,v+1+n);
		reverse(v+1,v+n+1);
		for(int i = 1;i <= n;i++)
			ans += 1ll * (v[i]/vmax) * (n - 2*i + 1),m[i] = v[i] % vmax,v[i] %= vmax;
		sort(m+1,m+1+n);
		int lm = unique(m+1,m+1+n) - m - 1;
		for(int i = 1;i <= n;i++)
		{
			v[i] = lower_bound(m+1,m+lm+1,v[i]) - m;
			ans -= ask(v[i]-1); updata(v[i]);
		}
		out(ans); ex;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/83591248