牛客小白月赛15 A~E 简要题解

A 斑羚飞渡 :

n n n只斑羚,每只斑羚跳跃的最远距离为 x [ i ] x[i] x[i],斑羚在别人的背上起跳的最远距离为 y [ i ] y[i] y[i],峡谷的两岸的距离为 s s s,问在最好情况下,有几只斑羚可以用别人的背当跳板跳到对岸,但由于斑羚的先天原因(主要是太肥),只能把别人当跳板一次

s < = 1000000000 ; x [ i ] , y [ i ] < = s ; 不 保 证 x [ i ] < y [ i ] s<=1000000000; x[i],y[i]<=s; 不保证x[i]<y[i] s<=1000000000;x[i],y[i]<=s;x[i]<y[i]

分析:

分类讨论
x i > = s x_i>=s xi>=s,答案+1,然后不管了
x i + y i > = s x_i+y_i>=s xi+yi>=s,有可能做贡献,用数组 a i a_i ai存起来
x i + y i < s x_i+y_i<s xi+yi<s,不可能做贡献,给别人当跳板,将 x i x_i xi用个 m u l t i s e t multiset multiset存起来

a i a_i ai按x升序排序,x相同时y降序排列
枚举 a i a_i ai,判断 m u l t i s e t multiset multiset是否存在 > = s − a i , y >=s-a_{i,y} >=sai,y的数,有的时候取最小,然后贡献+1,将对应的数从 m u l t i s e t multiset multiset中删去
m u l t i s e t multiset multiset不存在时则将 a i , x a_{i,x} ai,x给别人当跳板

代码:

#include 
#include 
#include 
#include 
#include  
#include 
#include 

#define N 1000005

using namespace std;

struct Node { int x, y; }c[N];
int n, s, cnt, ans;

multiset  cdp;

bool cmp(Node aa, Node bb)
{
	if (aa.x == bb.x) return aa.y > bb.y; 
	return aa.x < bb.x;
}

int main()
{
	scanf("%d %d", &n, &s);
	int a, b;
	for (int i = 1; i <= n; i++) 
	{
	    scanf("%d %d", &a, &b);
        if (a >= s) ++ans;
           else if (a + b >= s) c[++cnt].x = a, c[cnt].y = b;
             else cdp.insert(a);
	}
	sort(c + 1, c + cnt + 1, cmp);
	for (int i = 1; i <= cnt; i++) 
	{
	    multiset ::iterator num = cdp.lower_bound(s - c[i].y);
        if (num == cdp.end()) cdp.insert(c[cnt].x); else ++ans, cdp.erase(num);	
	}
	printf("%d\n", ans);
	return 0;
} 

B 诡异的因数 :

给出 T T T个询问,每个询问给出一个数n,问n的因子个数。

1 ≤ n , T ≤ 1 0 4 1≤n,T≤10^4 1n,T104

分析:

暴力就完事了

代码:

#include 
 
using namespace std;
 
int T, n;
 
int main()
{
    scanf("%d", &T);
    for (; T; T--)
    {
        scanf("%d", &n);
        int num = 0;
        for (int i = 1; i * i <= n; i++)
            if (n % i == 0)
            {
                num++;
                if (i * i != n) num++;
            }
        printf("%d\n", num);
    }
    return 0;
}

C 表单:

小T一开始有一张一共有n个字符串的字符串表,
现在他进行以下操作Q次:
(1):给字符串表中加入一个字符串s。
(2):给字符串表去重,输出去掉字符串的数量。

1 ≤ n , Q ≤ 5 × 1 0 5 1≤n,Q≤5×10^5 1n,Q5×105
对于所有输入的字符串长度<=40

分析:

用set存,每次遇到相同的累加一下,回答询问的时候直接输出然后清零继续下一次统计
这题特别有毛病。你的指令判断 0 / 1 0/1 0/1不用%d必WA。。

代码:

#include 
 
#define N 1000005
 
using namespace std;
 
set  s;
string aa;
int n, m, cnt, opt;

int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        cin >> aa;
        if (s.count(aa) == 1) cnt++; else s.insert(aa);
    }
    while (m--)
    {
        scanf("%d", &opt);
        if (opt == 1)
        {
            cin >> aa;
            if (s.count(aa) == 1) cnt++; else s.insert(aa);
        }
        else
        {
            printf("%d\n", cnt);
            cnt = 0;
        }
    }
    return 0;
}

D 分数的运算 :

两个分数x1/y1, x2/y2,求他们的和差积商

1 ≤ x 1 , y 1 , x 2 , y 2 ≤ 1 0 5 1≤x1,y1,x2,y2≤10^5 1x1,y1,x2,y2105
保证答案≤long long且分数有意义。

分析:

分别都通分一下然后求个gcd没了
注意一下正负性

代码:

#include 

using namespace std;

typedef long long ll;

ll gcd(ll aa, ll bb)
{
    return bb ? gcd(bb, aa % bb) : aa;
}

ll ax, ay, bx, by;
ll xx, yy, zz;

int main()
{
	scanf("%lld %lld %lld %lld", &ax, &ay, &bx, &by);
    xx = ax * by + bx * ay;
    yy = ay * by;
    if (xx < 0 && yy > 0) printf("-"); 
    if (xx > 0 && yy < 0) printf("-"); 
    xx = abs(xx); yy = abs(yy);
    zz = gcd(xx, yy);
	printf("%lld %lld\n", xx / zz, yy / zz);
	
	xx = ax * by - bx * ay;
	yy = ay * by;
    if (xx == 0) printf("0 0\n"); 
	else {
	    if (xx < 0 && yy > 0) printf("-"); 
        if (xx > 0 && yy < 0) printf("-"); 
        xx = abs(xx); yy = abs(yy);
        zz = gcd(xx, yy);
        printf("%lld %lld\n", xx / zz, yy / zz);
    }
    
    xx = ax * bx;
	yy = ay * by;
	if (xx < 0 && yy > 0) printf("-"); 
    if (xx > 0 && yy < 0) printf("-"); 
    xx = abs(xx); yy = abs(yy); 
	zz = gcd(xx, yy);
	printf("%lld %lld\n", xx / zz, yy / zz);
	
	xx = ax * by;
	yy = bx * ay;
	if (xx < 0 && yy > 0) printf("-"); 
    if (xx > 0 && yy < 0) printf("-"); 
    xx = abs(xx); yy = abs(yy); 
	zz = gcd(xx, yy);
	printf("%lld %lld\n", xx / zz, yy / zz);	 
	return 0;
} 

E 希望:

等离子炮有n个操作信号,第i个操作信号的强度为b[i]。总体强度为各操作信号的强度之和。
由于有些信号太弱了了 (强度<0),水宝宝想把它们删除。但是水宝宝自己不会删除信号,所以他找来了同船的队友帮忙。
有 m位队友,第ii 位队友只会删除编号在 L[i] 和 R[i]之间的信号,且每删除一个信号,花费 C[i]格能量。飞船一共有 k格能量,问他在请队友删除完信号后,总体强度最大是多少。
1 ≤ n , m ≤ 1 0 5 ; 1 ≤ k ≤ 500 ; 1 ≤ C [ i ] ≤ 500 ; 1 ≤ L [ i ] ≤ R [ i ] ≤ n ; − 1 0 9 ≤ b [ i ] ≤ 1 0 9 1≤n,m≤10^5;1≤k≤500;1≤C[i]≤500;1≤L[i]≤R[i]≤n;-10^9≤b[i]≤10^9 1n,m105;1k500;1C[i]500;1L[i]R[i]n;109b[i]109

分析:

总强度 s u m = b 1 + b 2 + . . . + b n − 1 + b n sum=b_1+b_2+...+b_{n-1}+b_n sum=b1+b2+...+bn1+bn
假如我删了一个 b k b_k bk b k b_k bk必定要 < 0 <0 <0,那么我们总强度提升了 − b k -b_k bk
那么我们设 d p i dp_{i} dpi表示花费了 i i i格能量我所能提升的最大强度,
问题转化成01背包,最后答案就是 s u m + m a x ( d p i ) sum+max(dp_i) sum+max(dpi)
对于将一个操作 ( l , r , x ) (l,r,x) (l,r,x),用线段树的很多个结点去表示,
如果结点 o o o所代表的区间 [ l 1 , r 1 ] [l1,r1] [l1,r1]能被包含在 [ l , r ] [l,r] [l,r]内,那么我们就判断更新这个结点记录的值 a o a_{o} ao
a o a_{o} ao就是操作区间 ( l 1 , r 1 ) (l1,r1) (l1,r1)的每一个数,我都可以用代价 a o a_{o} ao删掉,而且 a o a_{o} ao是对 k ∈ [ l , r ] k∈[l,r] k[l,r]都通用的最小代价
然后对于一个 b i < 0 b_i<0 bi<0而言,我们删掉它的最小代价,就是所有包含它的区间的 a o a_{o} ao m i n min min
时间复杂度就是 O ( n ( l o g n + k ) ) O(n(logn+k)) O(n(logn+k))

代码:

#include 
#include 
#include 
#include 
#include 
#include 

#define lson(x) x * 2
#define rson(x) x * 2 + 1

#define inf 0x3f3f3f3f
#define N 100005
#define M 505

using namespace std;

typedef long long ll;

struct Node { int num; }C[N*5];
int a[N], n, k, m, tot, cdp;
ll dp[M], sum;

void Build(int x, int l, int r)
{
	C[x].num = inf;
	if (l == r) return;
	int mid = (l + r) >> 1;
	Build(lson(x), l, mid);
	Build(rson(x), mid + 1, r);
}

void change(int x, int l, int r, int liml, int limr, int cnum)
{
	if (l > limr || r < liml) return;
	int mid = (l + r) >> 1; 
	if (liml <= l && r <= limr) { C[x].num = min(C[x].num, cnum); return; }
	change(lson(x), l, mid, liml, limr, cnum);
	change(rson(x), mid + 1, r, liml, limr, cnum);
}

void Get_num(int x, int l, int r, int pos)
{ 
	cdp = min(cdp, C[x].num); 
	if (l == r) return; 
	int mid = (l + r) >> 1;
	if (pos <= mid) Get_num(lson(x), l, mid, pos);
	           else Get_num(rson(x), mid + 1, r, pos);
}

int main()
{
	scanf("%d %d %d", &n, &k, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), sum = (ll)sum + a[i];
	Build(1, 1, n); 
    int l, r, x;
    for (int i = 1; i <= m; i++)
    {
    	scanf("%d %d %d", &l, &r, &x);
    	if (l > r) swap(l, r);
		change(1, 1, n, l, r, x);
	}
	for (int i = 1; i <= k; i++) dp[i] = -inf;
	ll maxnum = -inf;
	for (int i = 1; i <= n; i++) 
	    if (a[i] < 0) 
		{
			a[i] = -a[i]; cdp = inf; Get_num(1, 1, n, i);
	        for (int j = k; j >= cdp; j--) dp[j] = max(dp[j], dp[j - cdp] + a[i]), maxnum = max(maxnum, dp[j]);
	    }
	printf("%lld\n", sum + maxnum);
	return 0;
} 

你可能感兴趣的:(C++,暴力/枚举/模拟,线段树)