2019牛客暑期多校训练营(第七场)

 

RANK:176  现场 AC:  ABCDJ 5题

补题情况:

题号 A B C D E F G H I J K L
状态 Ο Ο Ο Ο Ο Ο . . . Ο . .

A:暴力从前往后,找到尽量长且满足题意的前缀串,

然后切开,重新进行这个操作。保证正确性的同时 复杂度可行,虽然是 n^3但  其实是n^2  *20左右  。可以想一下,越切越短。

#include
using namespace std;
#define ll long long
const int maxn=1e5+7;
bool judge(string s,int len)
{
    for(int i=1;itemp)
            return false;
    }
    return true;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        string s;
        cin>>s;
        int len=s.length();
        int now=0;
        int flag=1;
        while(now

B:n>=3一定可,n<2一定不可分。。猜的结论,+百度验证  正确性。

然后就n==2是,只要b^2-4*a*c>=0  就是函数==0有解的,即可分。

#include 
#include 
using namespace std;
typedef long long ll;
ll a[1010];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=n;i>=0;i--)  
            scanf("%lld",&a[i]);
        if(n>2)
        puts("No");
        else if(n==2)
        {
            if((a[1]*a[1])>=ll(4)*a[2]*a[0])
                puts("No");
            else  
                puts("Yes");
        }
        else
        {
            puts("Yes");
        }
    }
    return 0;
}

C:

有一个肯定对的贪心策略:

从高到底枚举所有树,然后从代价小的树开始砍,直到满足条件,就停止。记录每次花费,找出最小的就是答案。这个过程暴力肯定会T。On^2。

我们发现C的范围只有1-200.

很容易地,我们就会想到开一个200的桶,c[i]表示花费为c的树有几颗。c2[j]表示花费为j的树,且比当前树高的树有几颗

这样我们从树高到低枚举的时候,花费== 砍去比当前高的所有树的花费:temp+砍去还需再砍的树直到满足条件的花费:cost。

其中  temp从高到低枚举的时候可以记录下来,cost可以从1-200枚举,now为花费为i的树还剩几颗(总棵树减去必须要砍的,即比当前高度高的树)。贪心砍 直到满足条件。

其余部分比较简单,看代码就行,复杂度0(N*200).

注意 :种类不同的树高度可能相同,要把一个高度的树当成一个整体去处理。(因为肯定不砍最高的树,才是最优的贪心)

#include 
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair pii;
typedef pair pll;
typedef pair pdd;
#define F first
#define S second
const ll INF64=8000000000000000000LL;
const int INF=0x3f3f3f3f;
const ld PI=acos(-1);
const ld eps=1e-9;
const ll MOD=ll(1e9+7);
const int M = 1e5 + 10;
void modup(int&x){if(x>=MOD)x-=MOD;}
//unordered_mapmp;
struct node
{
    ll h,c,p,val,id,mp;
}T[M];
ll c1[210],c2[210];
bool cmp(node a,node b)
{
    return a.h=1;i--)
        {
            c2[T[i].c]+=T[i].p;
            sp+=T[i].p;
            if(T[i].h==T[i-1].h)
            { 
                continue;
            }
            ll del=numt-ap-(sp*2-1);
            if(del<=0)del=0;
            ll ans=0,cost=0;
        //  printf("%lld  %lld  %lld  %lld\n",numt,ap,del,sp);
            for(int j=1;j<=200;j++)
            {
                ll now=c1[j]-c2[j];
                ans+=now;
                cost+=now*j;
                if(ans>=del)
                {
                    cost-=(ans-del)*j;
                    break;
                }
            }
      
            ap+=sp;
            sp=0;
        //  printf("****  %lld   %lld   %lld\n",cost,temp,mi);
            mi=min(cost+temp,mi);
            int k=i;
            while(T[k].h==T[k+1].h)
                temp+=T[k].c*T[k].p,k++;
            temp+=T[k].c*T[k].p;
        //  printf("------%lld  %lld  %lld  %lld\n",cost,temp,T[k].c,T[k].p);
        }
    /*  for(int i=1;i<=200;i++)
        {
            if(c2[i])
            printf("+++--%d--%lld\n",i,c2[i]);
        }*/
        printf("%lld\n",mi);
    }
    return 0;
}

D:判断数位是否大于p,等于则 先输出p,再补0

否则输出T_T;

#include 
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair pii;
typedef pair pll;
typedef pair pdd;
#define F first
#define S second
const ll INF64=8000000000000000000LL;
const int INF=0x3f3f3f3f;
const ld PI=acos(-1);
const ld eps=1e-9;
const ll MOD=ll(1e9+7);
const int M = 1e5 + 10;
void modup(int&x){if(x>=MOD)x-=MOD;}
//unordered_mapmp;
int main()
{
    int n,p;
    scanf("%d%d",&n,&p);
    int w=0;
    int q=p;
    while(p)
    {
        p/=10;
        w++;
    }
    if(n

E:思路很简单,直接建一颗权值线段树  满足支持离散化,查询第k大,且区间更新操作.

但要注意的点很多,着重讲一下离散化操作。

我们线段树每个结点维护的是( S[l-1] , S[r] ],左开右闭数值区间内 数的个数。

S 是离散化之前,排好序的数组。比如:

[1,5]  [6,10]  离散化后  :

[1,2],[3,4];

区间维护:

比如  (1,4) 的区间

左儿子:(1,2)  维护的是( S[1]-S[2] ]区间内数的个数  (即区间 ( 1 ,5 ]  );

右儿子:(3,4)维护的是( S[2]-S[4] ]区间内数的个数(即区间(6,10  ]  );

我们发现即使这样做也会漏掉询问区间的左端点,所以我们只需要在离散化之前把所有左区间同时-1;

就能完美的解决这个问题!

#include 
using namespace std;
typedef long long ll;
typedef long double ld;
const int INF=0x3f3f3f3f;
const ld PI=acos(-1);
const ld eps=1e-9;
const ll MOD=ll(1e9+7);
const int M = 8e5 + 10;
void modup(int&x){if(x>=MOD)x-=MOD;}
//unordered_mapmp;
int L[M],R[M];
#define ls o<<1
#define rs o<<1|1
#define m (l + r) / 2
ll lz[M<<2],st[M<<2];
int S[M];
void pushdown(int o,int l,int r)
{
	if(lz[o])
	{
		lz[ls]+=lz[o];
		lz[rs]+=lz[o];
		st[ls]+=lz[o]*(S[m]-S[l-1]);
		st[rs]+=lz[o]*(S[r]-S[m]);
		lz[o]=0;
	}
}
void up(int o,int l,int r,int x,int y)//左开右闭 
{
//	printf("%d %d  %d\n",l,r,o);
	if(x<=l&&r<=y)
	{
		st[o]+=(S[r]-S[l-1]);//每个结点维护的是(l-1,r]间数的数量  这样才能保证不重不漏  
		lz[o]++;
		return ;
	}
	pushdown(o,l,r);
	if(x<=m)up(ls,l,m,x,y);
	if(y>m)up(rs,m+1,r,x,y);
	st[o]=st[ls]+st[rs];
}
int qry(int o,int l,int r,ll x)//第x大 
{
//	printf("++++%d %d  %d   %d  %d\n",l,r,st[ls],st[rs],x);
	if(l==r)
	{
		int k=st[o]/(S[r]-S[l-1]);//这个区间每个数出现几次 
		return (x+k-1)/k+S[l-1];//(v+k-1) -这个区间从左往右第几个是结果--对应离散化前 
	}
	pushdown(o,l,r);
	if(st[ls]>=x)return qry(ls,l,m,x);
	return qry(rs,m+1,r,x-st[ls]);
}
int main()
{
	int sz=0,n;
    scanf("%d",&n);
    ll x1,x2,x,y1,y2,y,a1,a2,b1,b2,c1,c2,m1,m2;
    scanf("%lld%lld%lld%lld%lld%lld",&x1,&x2,&a1,&b1,&c1,&m1);
    scanf("%lld%lld%lld%lld%lld%lld",&y1,&y2,&a2,&b2,&c2,&m2);
    L[1]=min(x1,y1);R[1]=max(x1,y1)+1;L[2]=min(x2,y2);R[2]=max(x2,y2)+1;
    S[++sz]=L[1];S[++sz]=L[2];S[++sz]=R[1];S[++sz]=R[2];
    for(int i=3;i<=n;i++)
    {
        x=(a1*x2+b1*x1+c1)%m1;
        y=(a2*y2+b2*y1+c2)%m2;
        x1=x2,y1=y2;
        x2=x,y2=y;
        L[i]=min(x,y);// 维护的是左开右闭区间
        R[i]=max(x,y)+1;
        S[++sz]=L[i];S[++sz]=R[i];
    }
    sort(S+1,S+1+sz);
    sz=unique(S+1,S+1+sz)-S-1;
    ll num=0;
    for(int i=1;i<=n;i++)
    {
        L[i] = lower_bound(S + 1, S + 1 + sz, L[i]) - S;
        R[i] = lower_bound(S + 1, S + 1 + sz, R[i]) - S;
        up(1,1,sz,L[i]+1,R[i]);
//每个结点维护的是左闭右开区间 所以更新区间 左端点要+1  这样查询时才不会出错 
        printf("%d\n",qry(1,1,sz,st[1]/2+(st[1]&1)));
    }
    return 0;
}

F:遍历每个石头,算贡献。

//KX
#include 
using namespace std;
typedef long long ll;
typedef double db;
//unordered_mapmp;
const int M= 2e5+7;
#define pb push_back
vectorv[M];
sets;
//set从小到大排列 
ll ans;
ll E[M],L[M],C[M];
ll b1[M],b2[M];
//树状数组b1存 长度i的时间段的个数
//树状数组b2存 长度i的时间段的长度
//可以用线段树代替,但没必要,因为只需要单点更新和前缀区间查询 
void add(int x,int d)
{
	while(x0)b1[x]++;else b1[x]--;
		b2[x]+=d;
		x+=x&(-x);
	}
	return ;
}
ll qu1(int x)//个数 
{
	ll ret=0;
	while(x)
	{
		ret+=b1[x];
		x-=x&(-x);
	}
	return ret;
}
ll qu2(int x)//长度 
{
	ll ret=0;
	while(x)
	{
		ret+=b2[x];
		x-=x&(-x);
	}
	return ret;
}
void Add(int x)
{
	if(s.size()==0)
	{
		s.insert(x);
		return ;
	}
	auto p = s.lower_bound(x);
	if(p==s.begin())
	{
		int w=(*p)-x;
		add(w,w);//加入的时间段刚好是区间长度减一,因为起点要吸收能量,不能增加 
	}
	else if(p==s.end())
	{
		int w=x-(*prev(p));
		add(w,w);
	}
	else
	{
		int pr=x-(*prev(p));
		int nt=*p-x;
		add(pr,pr);
		add(nt,nt);//加上2个小的区间 
		add(nt+pr,-(nt+pr));//删除原来整个的区间 
	}
	s.insert(x);
	return ;
}
void Del(int x)
{
	if(s.size()==1)
	{
		s.erase(x);
		return ;
	}
//	puts("ok1");
	
	auto p = s.find(x);//时间点一定是不同的,找的迭代器是唯一确定的 
//	printf("%d        %d----%d\n",*s.begin(),s.size(),*p);
	if(p==s.begin())
	{
		int w=(*next(p))-x;
		add(w,-w);//加入的时间段刚好是区间长度减一,因为起点要吸收能量,不能增加 
	}
	else if(next(p)==s.end())
	{
		int w=x-(*prev(p));
		add(w,-w);
	}
	else
	{
		int pr=(*p)-(*prev(p));
		int nt=(*next(p))-(*p);
		add(pr,-pr);
		add(nt,-nt);//删除原来2个小的区间 
		add(nt+pr,(nt+pr));//加上整个的区间 
	}
//	puts("ok2");
	s.erase(x);//唯一删除 
	return ;
}
void init(int n)
{
    memset(b1,0,sizeof(b1));
    memset(b2,0,sizeof(b2));
    for (int i=1;i<=n+1;i++) v[i].clear();
    s.clear(); ans = 0;
}
int main()
{
	/*
	ios::sync_with_stdio(false);
  	cin.tie(0);*/
 	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
	int CC;
	cin>>CC;
	for(int Case=1;Case<=CC;Case++)
	{
		int n,m,e,l,c;
		scanf("%d",&n);
		init(n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&E[i],&L[i],&C[i]);	
		}
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
		{
			int tt,ss,TT;
			scanf("%d%d%d",&tt,&ss,&TT);
			v[ss].pb(tt),v[TT+1].pb(-tt);
		}
		for(int i=1;i<=n;i++)
		{
		//	puts("ok");
			for(auto x:v[i])//vector的迭代器相当于下标 
			{
			//	printf("%d   %d\n",i,x);
				if(x>0)Add(x);
				else Del(-x);				
			}
		//	puts("ok22222");
			if(!s.size())continue;
			auto p=s.begin();
		//	printf("zzzzzzzzzz   : %d\n",*p);
			ans+=min(C[i],((*p))*L[i]+E[i]);
			if(L[i]==0)continue;
			int d=C[i]/L[i];
		//	printf("+++++++  %lld  %lld\n",ans,i);
			ans+=qu2(d)*L[i]+(s.size()-1-qu1(d))*C[i];
		//	printf("+++++++  %lld  %lld\n\n",ans,i);
		}
		printf("Case #%d: %lld\n",Case,ans);
		//初始化 
	}
   	return 0;
}


J:  字符串模拟A+B;先翻转再操作。

#include
using namespace std;
#define ll long long
const int maxn=1e5+7;
char s[12];
char ss[12];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s %s",&s,&ss);
        int len1=strlen(s);
        int len2=strlen(ss);
        reverse(s,s+len1);
        reverse(ss,ss+len2);
    //  printf("%s %s\n",s,ss);
        ll a=0,b=0;
        ll base=1;
        for(int i=len1-1;i>=0;i--)
        {
            a+=base*(s[i]-'0');
            base*=10;
        }
        base=1;
        for(int j=len2-1;j>=0;j--)
        {
            b+=base*(ss[j]-'0');
            base*=10;
        }
        a+=b;
        int flag=1;
        while(a)
        {
            if(a%10!=0)
                flag=0;
            if(a%10==0&&flag)
            {
                a/=10;
                continue;
            }
            printf("%lld",a%10);
            a/=10;
        }
        printf("\n");
    }
}

 

你可能感兴趣的:(多校----牛客/hdu)