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");
}
}