本人太弱只能打div2,当时只做出来三道题而且巨慢,于是掉了rating = = 后期补全了剩下两题的代码,还是发到博客上来看看吧…
A. Joysticks
题意简述:有两个电池有一些电量,每个单位时间充电会给它加1的电量,不充会减少2的电量,若电量为1你必须给它充电,问你最多能使它们保持有电多久?
数据范围:
a,b≤ 100
题解:贪心考虑每个单位时间都选电量最好的充就好了,然而有个弥天巨坑是两个电池都为1时要输出0……整个机房的人都被坑了,我也WA了三次,还是某老司机看群才知道的,浪费好多时间简直剧毒A题= =
Code
#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int a,b;
int main(){
scanf("%d%d",&a,&b);
if(a==1 && b==1){
printf("%d",0);
return 0;
}
for(int i=1;;i++){
if(a<b) a++,b-=2;
else b++,a-=2;
if(a<=0 || b<=0){
printf("%d",i);
return 0;
}
}
return 0;
}
B. Beautiful Paintings
题意简述:给你一个数列a包含n个数,每两个相邻的数之间若满足
ai+1<ai
,则会产生一点贡献,你可以任意排列这n个数来使得得到最大的贡献值。
数据范围:
n≤ 1000
题解:同样考虑贪心,把数列分成k段递增的子列了,且k尽可能相同大小,只要让相同的数隔得远一点就好了,用桶暴力统计就可以了……数据范围兹瓷
n2
,然而机房大神zyj表示用n减去最多的出现次数就好了,可能还要讨论一下有多个数出现最多次?反正我太弱只能暴力了。
Code
#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 1000+5
using namespace std;
int a[maxn],pd[maxn],t[maxn];
int n,ans,tmp,last;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),t[a[i]]++;
sort(a+1,a+1+n);
bool flag=0;
for(;!flag;){
flag=1; tmp=0;
for(int i=1;i<=1000;i++)
if(t[i]) t[i]--,tmp++,flag=0;
if(!flag) ans+=tmp-1;
}
printf("%d",ans);
return 0;
}
C. Watchmen
题意简述:平面直角坐标系上给你n个点,求曼哈顿距离等于欧氏距离的点对的个数
数据范围:
(n≤ 200000,|xi|,|yi|≤ 109)
题解:就是统计有多少个点对同一行或者一列上,可以直接对横坐标排序一次,扫一遍统计,再对纵坐标排序一次,扫一遍统计。同一行同一列的乘上一个组合数
(k2)
。注意有一些点坐标相同,在第一次统计的时候顺便统计出来,最后删去即可。
(PS:这个重复点的小bug我调了好久= = 简直sb了,比别人晚交十多分钟,掉了200+分)
(PSS:代码好丑)
Code
#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 200000+5
using namespace std;
struct Point{
int x,y;
}a[maxn];
long long C[maxn][3];
long long ans,ans2;
int n,tmp,tmp2;
bool cmp1(const Point &s,const Point &b){
if(s.x==b.x) return s.y<b.y;
return s.x<b.x;
}
bool cmp2(const Point &s,const Point &b){
if(s.y==b.y) return s.x<b.x;
return s.y<b.y;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
for(int i=0;i<=n;i++)
C[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=min(2,i);j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
sort(a+1,a+1+n,cmp1);
a[0].y=a[0].x=-23333333; tmp=1; tmp2=1;
for(int i=1;i<=n;i++){
if(a[i].x==a[i-1].x && a[i].y==a[i-1].y) tmp2++;
else ans2+=C[tmp2][2],tmp2=1;
if(a[i].x==a[i-1].x) tmp++;
else ans+=C[tmp][2],tmp=1;
}
if(tmp>=2) ans+=C[tmp][2]; tmp=1;
if(tmp2>=2) ans2+=C[tmp2][2];
sort(a+1,a+1+n,cmp2);
for(int i=1;i<=n;i++){
if(a[i].y==a[i-1].y) tmp++;
else ans+=C[tmp][2],tmp=1;
}
if(tmp>=2) ans+=C[tmp][2];
printf("%lld",ans-ans2);
return 0;
}
D. Image Preview
题意简述:你有n张摆成环形的图片,有些是横着摆的,有些是竖着摆的,你的手机是保持竖着的(没看懂英文瞎口胡的,意思差不多就好23333),你从第一张图片开始看,每张图片若与你手机方向不同,你需要花
b+1
的时间来观看它,否则你只需要花
1
的时间看,同时相邻的图片之间移动需要花费
数据范围:
题解:把环拓展一下成链,显然我只能看包括一号点的中间一段图片,这样就可以枚举左端点,然后二分右端点了,对于已经固定的两个端点,显然只有两种移动方式,先到左边和先到右边之后再回来,取最小值来完成对二分的判定。当然你也可以直接用两个指针扫过去就好了,不过打的时候我两个指针扫的一些细节没想清,没打完这题就爆炸了= =。
Code
#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 1000000+5
using namespace std;
char s[maxn];
int step[maxn];
int n,a,b,T,N,ans;
bool check(int L,int R){
if(R<n+1) return true;
int tmp1=0,tmp2=0;
tmp1=tmp2=step[R]-step[L-1];
tmp1+=(n-L+1)*a; tmp1+=(R-L)*a;
tmp2+=(R-n-1)*a; tmp2+=(R-L)*a;
return min(tmp1,tmp2)<=T;
}
int div2(int L,int R){
int l=n+1,r=R,res=l;
if(!check(L,n+1)) return -1;
while(l<=r){
int mid=(l+r)>>1;
if(check(L,mid)) l=mid+1,res=mid;
else r=mid-1;
}
return res-L+1;
}
int main(){
scanf("%d%d%d%d",&n,&a,&b,&T);
scanf("%s",s+1); N=n*2;
for(int i=n+1;i<=N;i++) s[i]=s[i-n];
for(int i=1;i<=N;i++)
if(s[i]=='w') step[i]=step[i-1]+b+1;
else step[i]=step[i-1]+1;
for(int i=1;i<=n+1;i++){
int tmp=div2(i,N);
if(tmp>0) ans=max(ans,tmp);
}
printf("%d",min(ans,n));
return 0;
}
E. Table Compression
题意简述:给你一个
n∗m
的矩阵,要求对里面的元素进行离散化,保证每一行和每一列之间的元素大小关系不变,同时要求最后的矩阵最大值最小,输出离散化后的矩阵。
数据范围:
n∗m ≤ 106, 1≤ ai,j ≤ 109
题解:整个矩阵满足一个拓扑关系,直接对每一行排序然后添边,相同的元素并查集合并,之后拓扑排序+DP就OK了。然后机房大神TB表示不用添边也可以,本蒟蒻还没仔细想怎么实现= =
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 2000000+5
using namespace std;
struct Node{
int val,pos;
}tmp[maxn];
struct Graph{
int u[maxn],v[maxn],head[maxn],nxt[maxn];
int ind;
Graph(){ ind=0; memset(head,-1,sizeof(head)); }
void addedge(int x,int y){
u[ind]=x; v[ind]=y; nxt[ind]=head[x]; head[x]=ind++;
}
}G,Gn;
int mat[maxn],d[maxn],du[maxn],stack[maxn],f[maxn];
int n,m,N,cnt,top;
inline int in(){
int x=0;
char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
bool cmp(const Node &s,const Node &b){
if(s.val==b.val) return s.pos<b.pos;
return s.val<b.val;
}
int find(int x){
if(d[x]==x) return x;
return d[x]=find(d[x]);
}
void unite(int x,int y){
int a=find(x),b=find(y);
if(a==b) return;
d[a]=b;
}
void build(int lim){
sort(tmp+1,tmp+1+lim,cmp);
for(int i=2;i<=lim;i++)
if(tmp[i-1].val==tmp[i].val) unite(tmp[i-1].pos,tmp[i].pos);
else G.addedge(tmp[i-1].pos,tmp[i].pos);
}
void Toposort(){
for(int i=1;i<=N;i++)
if(find(i)==i && !du[i]) stack[++top]=i,f[i]=1;
while(top){
int x=stack[top--];
for(int i=Gn.head[x];i!=-1;i=Gn.nxt[i]){
int v=Gn.v[i];
if(!(--du[v])) stack[++top]=v;
f[v]=max(f[v],f[x]+1);
}
}
}
int main(){
scanf("%d%d",&n,&m); N=n*m;
for(int i=1;i<=N;i++)
mat[i]=in(),d[i]=i;
for(int i=1;i<=N;i++){
tmp[++cnt].val=mat[i]; tmp[cnt].pos=i;
if(cnt==m){ cnt=0; build(m); }
}
for(int i=1;i<=m;i++){
for(int j=i;j<=N;j+=m)
tmp[++cnt].val=mat[j],tmp[cnt].pos=j;
cnt=0; build(n);
}
for(int i=1;i<=N;i++)
for(int j=G.head[i];j!=-1;j=G.nxt[j])
if(find(G.u[j])!=find(G.v[j])){
Gn.addedge(d[G.u[j]],d[G.v[j]]);
du[d[G.v[j]]]++;
}
Toposort();
for(int i=1;i<=N;i++){
printf("%d",f[d[i]]);
if(i%m==0) puts("");
else putchar(' ');
}
return 0;
}
Sad Story
因为A,C题一开始爆炸,D题开始的晚就没打完,导致机房排最后= = 然而之后结果一出来FST了一堆人……排名就往前面翻了一点。。。好吧然而这次题这么水,只切三道果然是掉rating了。。。。悲伤啊