这场么…又一次打炸掉了..(不知道为啥这场他们的手速都这么快,我在1:47用一个保证fst的算法过了D题pp结果也才400名)..最后加上D题fst…排名可想而知。(惨啊!qwq)
最终排名:rank813,rating-=41,目前rating:1764(好像这几场下来离紫名越来越远了啊有木有…)
P.S. 这场打完我还有一个教训…虽然打前两题手速要快,但是..太快就会GG啊。。
A. Fraction
题意:给你一个正整数n,问你能求得的最大的真分数且是既约分数且分子与分母的和为n的分数是多少,输出分子和分母。
思路&&题解:这一题我一上来看了1分30时我就打完了,然后交上去WA掉了..白白少了50分。最后再仔细看一遍题,发现我没有看到既约分数..最后加个GCD判一下就好了..
代码如下:
#include<bits/stdc++.h>
using namespace std;
long long n;
inline int gcd(int a,int b) {
return b?gcd(b,a%b):a;
}
int main() {
cin>>n;
if(n%2==0) {
int a=n/2-1,b=n/2+1;
while(gcd(a,b)!=1) {
a--;
b++;
}
cout<<a<<" "<<b<<endl;
}
else {
int a=n/2,b=n/2+1;
while(gcd(a,b)!=1) {
a--;
b++;
}
cout<<a<<" "<<b<<endl;
}
return 0;
}
B. Maxim Buys an Apartment
题意: Maxim想在一条街上买一幢公寓,这条街上有n幢公寓标号从左到右为1~n,Maxim定义一幢公寓为“好”的,只要和它相邻的公寓中至少有一幢已经有人住了。现在Maxim知道其中有k幢已经被人买走了且有人住了,但他不知道现在住了人的公寓的标号是多少,现在让你求最少有多少公寓是“好”的和最多有多少公寓是“好”的。
思路&&题解:首先,显然的是当k==n或k==0的时候,答案一定是0 0(显然)。否则最少的情况一定是k幢公寓从编号1开始是连着被买的,这样只有一幢公寓是好的。现在我们考虑最大多少幢,我们可以发现就从编号2开始每隔两个放一个,(即2,5,8….)(可以证明这是最优的),那么答案就是min(n-k,2*k)..我刚开始没特判k==0 WA了..还有一次瞎写了个算法GG了..
代码如下:
#include<bits/stdc++.h>
using namespace std;
long long n,k;
int main() {
cin>>n>>k;
if(n==k||k==0) {
cout<<"0 0"<<endl;
return 0;
}
cout<<"1 ";
if(k<=n/3)
cout<<k*2<<endl;
else
cout<<min(n-k,k*2)<<endl;
return 0;
}
C. Planning
题意:飞机场原定计划从第一分钟开始每分钟起飞一架飞机,但是由于某些原因导致前k分钟无法起飞飞机。给出每架飞机每延误一分钟的损失 costi ,问所有飞机都起飞后的最小损失为多少。
思路&&题解:这题只要贪心一下就好了。用一个堆(其实优先队列就行了233)..然后根据cost排列,一个一个赋值下去就行了2333..(其实挺好写的)
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=300050;
ll n,k;
struct node{
ll num;
int id;
bool operator < (const node &rhs) const {
return num<rhs.num;
}
};
priority_queue<node> a;
ll ans[maxn];
ll sum=0,x[maxn];
bool vis[maxn<<1];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++) {
cin>>x[i];
if(i<=k+1)
a.push((node){x[i],i});
}
for(int pos=k+1;pos<=k+n;pos++) {
node now=a.top();
a.pop();
ans[now.id]=pos;
sum+=now.num*(pos-now.id);
if(pos<n)
a.push((node){x[pos+1],pos+1});
}
cout<<sum<<endl;
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";
cout<<endl;
return 0;
}
D. Jury Meeting
题解:给你m个航班,然后使得n个特派员在0号城市开会,最少要让这n个特派员一起在这里待上k天,然后每个航班的航线、时间和花费已知,问你满足题意的最小花费。如果不满足,则输出-1。
思路&&题解:这题看起来有点难..实际上想通了挺简单的。只是当时我来不及打了,然后随便瞎糊了个暴力,没想到过pp了,(虽然最后不用想也知道肯定fst..qwq)。实际上这题就是贪心。只要将航班按时间排序,然后从前往后维护出发航班前缀最小值,然后从后往前维护返回航班的后缀最小值,最后枚举一下从哪天开始n个特派员都在0号城市就行了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200050;
struct Edge{
ll d,from,to,val;
}a[maxn];
ll n,m,k,sum,tot,vis[maxn],l[maxn],r[maxn],ans=1e18;
inline ll cmp(Edge a,Edge b) {
return a.d<b.d;
}
inline void fastIO() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
inline void init() {
memset(vis,0,sizeof vis);
memset(l,-1,sizeof l);
memset(r,-1,sizeof r);
}
int main() {
fastIO();
cin>>n>>m>>k;
init();
for(int i=1;i<=m;i++)
cin>>a[i].d>>a[i].from>>a[i].to>>a[i].val;
sort(a+1,a+m+1,cmp);
sum=tot=0;
for(int i=1;i<=m;i++) {
// cout<<i<<" "<<sum<<" "<<tot<<endl;
if(a[i].from==0)
continue;
if(vis[a[i].from]==0) {
vis[a[i].from]=a[i].val;
sum+=a[i].val;
tot++;
}
else {
if(vis[a[i].from]>a[i].val) {
sum-=vis[a[i].from];
vis[a[i].from]=a[i].val;
sum+=a[i].val;
}
}
if(tot==n)
l[i]=sum;
}
sum=tot=0;
memset(vis,0,sizeof vis);
for(int i=m;i>=1;i--) {
if(a[i].to==0)
continue;
if(vis[a[i].to]==0) {
sum+=a[i].val;
tot++;
vis[a[i].to]=a[i].val;
}
else {
if(vis[a[i].to]>a[i].val) {
sum-=vis[a[i].to];
sum+=a[i].val;
vis[a[i].to]=a[i].val;
}
}
if(tot==n)
r[i]=sum;
}
for(int i=m;i>=1;i--) {
if(r[i]==-1)
continue;
else {
if(r[i+1]==-1)
continue;
r[i]=min(r[i],r[i+1]);
}
}
for(int i=1;i<=m;i++) {
// cout<<i<<" "<<l[i]<<endl;
if(l[i]==-1)
continue;
int pos=-1,lim=a[i].d+k+1,lef=i+1,rig=m,mid;
while(lef<=rig) {
mid=(lef+rig)>>1;
if(a[mid].d>=lim) {
rig=mid-1;
pos=mid;
}
else
lef=mid+1;
}
if(pos==-1)
continue;
else if(r[pos]!=-1)
ans=min(ans,r[pos]+l[i]);
}
cout<<(ans==1e18?-1:ans)<<endl;
return 0;
}
E. Boredom
题意:一个n*n的网格图,有n个标记,每列只有一个标记,定义美丽的矩形为以两个标记所在位置构成的矩形(对角线的两个角)。q次询问,每次询问给你一个矩形,问有多少个美丽的矩形与该矩形相交。
思路&&题解:这题可以用树状数组+离线回答做。首先我们可以知道,如果一个区域内有n个标记,那么这n个标记可以组成n*(n-1)/2个矩形。我们可以将大矩形根据询问的范围将它分成九块,用一维树状数组维护四个角的点数,然后将询问先从左到右排序一遍,更新答案;然后将询问从右到左排序一遍继续更新答案,最后再弄一下就好了(详情请见代码)
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200050;
int n,m,l,d,u,r,a[maxn];
ll ans[maxn];
struct treearray {
ll val[maxn];
inline int lowbit(int x){return x&(-x);};
inline void clear(){memset(val,0,sizeof val);}
inline void add(int x){for(int i=x;i<=n;i+=lowbit(i))val[i]++;}
inline ll query(int x){ll res=0;for(int i=x;i>=1;i-=lowbit(i))res+=val[i];return res;}
}tx,ty;
struct que {
int l,d,r,u,id;
que(int l=0,int d=0,int r=0,int u=0,int id=0):l(l),d(d),r(r),u(u),id(id){}
}re[maxn];
inline bool cmP(que a,que b){return a.l<b.l;}
inline bool Cmp(que a,que b){return a.r>b.r;}
inline ll f(ll x){return 1LL*(x-1)*x/2LL;}
inline void fastIO(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);}
int main() {
fastIO();
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)cin>>l>>d>>r>>u,re[i]=que(l,d,r,u,i);
sort(re+1,re+m+1,cmP);
int pos=1;
for(int i=1;i<=n&&pos<=m;i++) {
while(re[pos].l==i&&pos<=m)ans[re[pos].id]+=f(tx.query(re[pos].d-1))+f(ty.query(n-re[pos].u)),pos++;
tx.add(a[i]),ty.add(n-a[i]+1);
}
tx.clear(),ty.clear();
sort(re+1,re+m+1,Cmp);
pos=1;
for(int i=n;i>=1&&pos<=m;i--) {
while(re[pos].r==i&&pos<=m)ans[re[pos].id]+=f(tx.query(re[pos].d-1))+f(ty.query(n-re[pos].u)),pos++;
tx.add(a[i]),ty.add(n-a[i]+1);
}
for(int i=1;i<=m;i++)ans[re[i].id]+=f(n),ans[re[i].id]-=f(re[i].l-1)+f(n-re[i].r)+f(n-re[i].u)+f(re[i].d-1);
for(int i=1;i<=m;i++)cout<<ans[i]<<endl;
return 0;
}