废话
感觉不是很难吧,也怪不得差评连连
要是下面的代码锅了还请通知一声,可能是贴错了qwq
T1
差不多是改编题而已啊,显然对于每一组序列的参数,当 增大时计算出来的数也肯定会增大,那么开一个小根堆维护一下就好了。
在洛谷试炼场找堆的专题,以及在提高(好像是?)一本bug通里面的贪心专题(可能是堆?)里面都能找到十分类似的题。
贴个代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 30010
#define ll long long
int n,m,k;
ll a[maxn][10];
struct node{ll x;int y;};
struct heap{
node dui[maxn];
int t;
heap():t(0){}
void up(int x)
{
while(x>1&&dui[x].x<dui[x/2].x)
{
swap(dui[x],dui[x/2]);
x>>=1;
}
}
void add(ll x,int y)
{
dui[++t]=(node){x,y};
up(t);
}
void down(int x)
{
if(x>t/2)return;
int ans=x;
if(dui[x*2].x<dui[x].x)ans=x*2;
if(x*2+1<=t&&dui[x*2+1].x<dui[ans].x)ans=x*2+1;
if(x!=ans)
{
swap(dui[x],dui[ans]);
down(ans);
}
}
node pop()
{
node re=dui[1];
dui[1]=dui[t--];
down(1);
return re;
}
}dui;
int tot[maxn];
void add(int x,int z)
{
ll sum=0,now=1;
for(int i=0;i<=n;i++)
sum+=(ll)a[x][i]*now,now*=(ll)z;
dui.add(sum,x);
}
int main()
{
scanf("%d %d %d",&k,&m,&n);k--;
for(int i=1;i<=m;i++)
for(int j=0,x;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=m;i++)
add(i,1),tot[i]++;
while(k--)
{
node x=dui.pop();
tot[x.y]++;
add(x.y,tot[x.y]);
}
printf("%lld",dui.pop().x);
}
T2
考场上打崩了祭
做法也不难想,将所有点按行从小到大排个序,遍历一次,考虑弄一个 ,对于一段点,我们压成一个区间存在 里面,每次加点的时候考虑与相邻的区间合并(就是这里炸了qwq)。然后往上一行走的时候,将所有区间的左右端点往内缩一格即可,缩没了的区间丢掉,然后在缩的时候统计一下答案就好了。
我觉得就是加点比较需要注意,别忘记搞一搞其他的细节就好了。
#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
using namespace std;
#define maxn 300010
#define it set<node>::iterator
int n;
struct node{
mutable int x,y;
node(int xx=0,int yy=0):x(xx),y(yy){}
bool operator <(const node b)const{return x<b.x;}
};
set<node> s;
node a[maxn];
void add(int x)
{
if(s.empty()){s.insert(node(x,x+1));return;}
it p=s.lower_bound(node(x));
if(p!=s.end()&&p->x==x)return;
if(x+2==p->x)
{
p->x-=2;
int x=p->x,y=p->y;
if(p!=s.begin())
{
p--;
if(p->y+1==x)
{
p->y=y;
p++;s.erase(p);
}
}
return;
}
if(p==s.begin()){s.insert(node(x,x+1));return;}
p--;
if(x<=p->y)return;
if(x==p->y+1)
{
p->y+=2;
int x=p->x,y=p->y;p++;
if(p!=s.end()&&p->x==y+1)
{
p->x=x;
p--;s.erase(p);
}
}
else s.insert(node(x,x+1));
}
long long ans=0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d %d",&a[i].x,&a[i].y);
sort(a+1,a+n+1);
int h=a[1].x,now=1;
while(now<=n||!s.empty())
{
while(a[now].x==h&&now<=n)add(a[now].y),now++;
it st=s.begin();
while(st!=s.end())
{
ans+=(st->y-st->x+1)/2;
st->x++;st->y--;
if(st->x>st->y)
{
it p=st;st++;
s.erase(p);
}
else st++;
}
h++;
if(s.empty())h=a[now].x;
}
printf("%lld",ans);
}
/*
附赠我的样例一组:
7
0 0
0 2
-1 1
-1 3
-1 5
4 0
4 2
ans=12
*/
T3
这个算是有一点水平的吧。
显然最后的排班方式肯定是形如
这样的循环,而且在每一个循环节里面每个保镖恰好出现一次(这样满足保镖不希望多工作
这个条件)。
那么设 为选择的所有保镖的 之和,即 。
那么对于任意一个选中的保镖 ,肯定满足 ,移项就是 。
那么看到这种柿子就不禁(??)想要对所有保镖以 为关键字排序了对吧!
排完序后,从小到大枚举保镖,设现在枚举到保镖 ,假设我们只在 ~ 这些保镖里面去选择,并且一定要选择第 个保镖,考虑此时的最优方案。
我们回到上面的柿子, ,这个柿子等价于: 。
因为我们强行选择了第 个保镖,而第 个保镖的 是比 ~ 这些保镖都大的,所以这个不等式的右边已经确定了,即 。
现在的目标是选尽可能少的保镖让 大于等于 。
那么肯定贪心得选 大的保镖嘛,那么要怎么选这个问题用数据结构大力维护然后二分就好了,但是由于它不卡我,所以我打了个暴力,稍稍优化了一下,每次从上一次的选择中转移过来而不是每次都从头选。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
#define maxn 500010
int n;
struct node{ll x,y;};
node a[maxn];
bool cmp(node x,node y){return (x.x+x.y)<(y.x+y.y);}
struct heap{
ll dui[maxn];
int t,type;
heap():t(0){}
bool check(ll x,ll y)
{
if(type==0)return x<y;
else return x>y;
}
void up(int x)
{
while(x>1&&check(dui[x],dui[x/2]))
{
swap(dui[x],dui[x/2]);
x>>=1;
}
}
void add(ll x)
{
dui[++t]=x;
up(t);
}
void down(int x)
{
if(x>t/2)return;
int ans=x;
if(check(dui[x*2],dui[x]))ans=x*2;
if(x*2+1<=t&&check(dui[x*2+1],dui[ans]))ans=x*2+1;
if(ans!=x)
{
swap(dui[x],dui[ans]);
down(ans);
}
}
ll top(){return dui[1];}
void pop(){dui[1]=dui[t--];down(1);}
bool empty(){return t==0;}
}dui1,dui2;
int ans=999999999;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld %lld",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmp);
dui1.type=0;dui2.type=1;
ll S=0,now;
for(int i=1;i<=n;i++)
{
now=a[i].x+a[i].y;
dui1.add(a[i].x);S+=a[i].x;
while(S-dui1.top()>=now)S-=dui1.top(),dui2.add(dui1.top()),dui1.pop();
while(S<now&&!dui2.empty())S+=dui2.top(),dui1.add(dui2.top()),dui2.pop();
if(S>=now)ans=min(ans,dui1.t);
}
if(ans==999999999)printf("-1");
else printf("%d",ans);
}
说出来你可能不信,第三题我在考场上忘记判无解输出 的情况,居然直接被它的 卡成 分?