最近感觉暑假在家好颓,效率直线下降,码代码速度慢,想题想不出。这次R2的题目也是过了很久才做完……但愿8月份到了学校情况会好些吧。
T1:TIANKENG’s restaurant
题目大意:给出n(n<=10000)个时间段(形式为hh:mm~hh:mm)和数字x,表示这个时间段会有x个客人来饭店就餐。每个客人需要一张椅子,问最少需要多少张椅子(一个客人离开后椅子可以立马给另一个客人坐)。不超过100组数据,保证时间段不会跨过凌晨0点。
题目分析:这题本质上就是区间加x,最后每一个数取个max。我们在开始时刻+1,在结束时刻后一分钟-1,然后从左往右扫一遍就可以求出答案。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1450;
int temp[maxn];
int T,n;
int Read()
{
int x=0;
char c=getchar();
while ( c<'0' || '9'<c ) c=getchar();
while ( '0'<=c && c<='9' ) x=(x<<3)+(x<<1)+(c-'0'),c=getchar();
return x;
}
int Get(int x,int y)
{
return 60*x+y;
}
int main()
{
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d",&T);
while (T--)
{
memset(temp,0,sizeof(temp));
scanf("%d",&n);
for (int i=1; i<=n; i++)
{
int x;
scanf("%d",&x);
int h=Read(),m=Read();
temp[ Get(h,m) ]+=x;
h=Read(),m=Read();
temp[ Get(h,m) ]-=x;
}
int ans=0,now=0;
for (int i=0; i<maxn; i++)
now+=temp[i],ans=max(now,ans);
printf("%d\n",ans);
}
return 0;
}
T2:TIANKENG’s rice shop
题目大意:餐馆提供n(n<=1000)种食物。现在有m(m<=1000)个客人在hh:mm时刻来餐馆排队,每个人会点某一种食物num(num<=10)份。厨师会按照顺序给他们做饭,他会用t(t<=10)分钟做好k(k<=5)份相同类型的食物,如果供应完当前客人之后还有多余的几份,他就会将剩下这些分给已经来排队的要点该类型食物的人(靠前优先),如果还有多余的则扔掉。假设每个客人在拿到num份食物后就会离开,求每个客人的离开时刻。客人抵达的时间不会跨过凌晨0点,不超过100组数据。
题目分析:这题就是个恶心的硬模拟。我们直接开一个n*m的数组维护一下每一种食物有哪些客人点即可,时间O(Tm),其中T是数据组数。这里还有一个很坑的地方:虽然客人抵达的时间顶多是当天23:59,但是他离开的时间可能是好几天后。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1010;
int id[maxn][maxn];
int head[maxn];
int tail[maxn];
int Type[maxn];
int Time[maxn];
int num[maxn];
int ans[maxn];
int T,n,t,k,m;
int Read()
{
int x=0;
char c=getchar();
while ( c<'0' || '9'<c ) c=getchar();
while ( '0'<=c && c<='9' ) x=(x<<3)+(x<<1)+(c-'0'),c=getchar();
return x;
}
int Get(int x,int y)
{
return 60*x+y;
}
void Print(int x)
{
int hh=x/60;
hh%=24;
int mm=x%60;
if (hh<10) printf("0");
printf("%d:",hh);
if (mm<10) printf("0");
printf("%d\n",mm);
}
int main()
{
freopen("2.in","r",stdin);
freopen("2.out","w",stdout);
scanf("%d",&T);
while (T--)
{
scanf("%d%d%d%d",&n,&t,&k,&m);
for (int i=1; i<=n; i++) head[i]=0,tail[i]=0;
for (int i=1; i<=m; i++)
{
int hh=Read(),mm=Read(),y;
scanf("%d%d",&Type[i],&y);
Time[i]=Get(hh,mm);
num[i]=y;
id[ Type[i] ][ ++tail[ Type[i] ] ]=i;
}
int nt=0;
for (int i=1; i<=m; i++) if (num[i])
{
nt=max(nt,Time[i]);
int x=Type[i];
while (num[i]>k) num[i]-=k,nt+=t;
nt+=t;
int Left=k;
while ( head[x]<tail[x] && Time[ id[x][ head[x]+1 ] ]<=nt-t )
{
head[x]++;
int y=id[x][ head[x] ];
if (num[y]<=Left)
{
Left-=num[y];
num[y]=0;
ans[y]=nt;
}
else
{
num[y]-=Left;
head[x]--;
break;
}
}
}
for (int i=1; i<=m; i++) Print(ans[i]);
if (T) printf("\n");
}
return 0;
}
T3:TIANKENG’s travel
题目大意:在一个平面图上给出起点,终点,以及n(n<=1000)个加油站。从一个点可以行驶到另一个和它欧几里得距离小于等于L的点,而且必须直线行驶。就是说如果两点的连线之间还有加油站,那么肯定行驶时必定会经过这个加油站。现在要求最小化从起点到终点所经过的加油站个数,不超过30组数据。
题目分析:这题我一开始没看见必须直线行驶这个条件,以为是
n2
暴力连边然后BFS,但我又觉得30组数据可能会超
(3∗107)
。于是我不停地想
nlog(n)
的做法,结果根本不会。后来我看了题解才发现要用
n2log(n)
判断三点共线……
由于两个点中间有加油站时不能直接连边,所以我们先将点按x坐标从小到大排序,相同则按y从小到大。这样点i连向点j的边
(i<j)
的极角就是
(−π2,π2]
,然后我们对一个点i的所有射出的边按极角大小排序,然后相同角度的只连该射线上的第一个点即可。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1010;
typedef long long LL;
struct edge
{
int obj;
edge *Next;
} e[maxn*maxn];
edge *head[maxn];
int cur;
int level[maxn];
int que[maxn];
int he,ta;
struct data
{
int dx,dy,id;
} line[maxn][maxn];
struct point
{
int X,Y,T;
} gas[maxn];
int G,n,l;
bool Comp1(point x,point y)
{
return x.X<y.X || ( x.X==y.X && x.Y<y.Y );
}
bool Comp2(data x,data y)
{
LL temp1=x.dy;
temp1*=y.dx;
LL temp2=y.dy;
temp2*=x.dx;
bool flag=( x.dx<y.dx || ( x.dx==y.dx && x.dy<y.dy ) );
return temp1<temp2 || ( temp1==temp2 && flag );
}
bool Judge(int x,int y)
{
LL temp1=gas[x].X-gas[y].X;
temp1*=temp1;
LL temp2=gas[x].Y-gas[y].Y;
temp2*=temp2;
temp1+=temp2;
temp2=l;
temp2*=temp2;
return temp1<=temp2;
}
void Add(int x,int y)
{
cur++;
e[cur].obj=y;
e[cur].Next=head[x];
head[x]=e+cur;
}
int Bfs()
{
int s,t;
for (int i=0; i<=n; i++)
{
level[i]=0;
if (!gas[i].T) s=i;
if (gas[i].T==n) t=i;
}
he=0,ta=1,que[1]=s;
while (he<ta)
{
int node=que[++he];
for (edge *p=head[node]; p; p=p->Next)
{
int son=p->obj;
if ( son && !level[son] )
{
level[son]=level[node]+1;
que[++ta]=son;
}
}
}
return level[t];
}
int main()
{
freopen("3.in","r",stdin);
freopen("3.out","w",stdout);
scanf("%d",&G);
while (G--)
{
scanf("%d%d",&n,&l);
n++;
scanf("%d%d",&gas[0].X,&gas[0].Y);
scanf("%d%d",&gas[n].X,&gas[n].Y);
for (int i=1; i<n; i++) scanf("%d%d",&gas[i].X,&gas[i].Y);
for (int i=0; i<=n; i++) gas[i].T=i;
sort(gas,gas+n+1,Comp1);
cur=-1;
for (int i=0; i<=n; i++) head[i]=NULL;
for (int i=0; i<n; i++)
{
for (int j=i+1; j<=n; j++)
{
line[i][j-i].id=j;
line[i][j-i].dx=gas[j].X-gas[i].X;
line[i][j-i].dy=gas[j].Y-gas[i].Y;
}
sort(line[i]+1,line[i]+n-i+1,Comp2);
if ( Judge(i,line[i][1].id) )
Add(i,line[i][1].id),Add(line[i][1].id,i);
for (int j=2; j<=n-i; j++)
{
LL temp1=line[i][j].dy;
temp1*=line[i][j-1].dx;
LL temp2=line[i][j-1].dy;
temp2*=line[i][j].dx;
if ( temp1!=temp2 && Judge(i,line[i][j].id) )
Add(i,line[i][j].id),Add(line[i][j].id,i);
}
}
int ans=Bfs();
if (ans) printf("%d\n",ans-1);
else printf("impossible\n");
}
return 0;
}
T4:TIANKENG’s restaurant(Ⅱ)
题目大意:给出一个只包含A~H,长度不超过 106 的字符串,输出一个最短的只包含A~H的答案串,使得答案串不是给定串的子串。如果有多组解,则输出字典序最小的那个。
题目分析:这题空间只有64M我还能说什么……当初一看这题很高兴因为这就是个SAM的裸题呀。我们把后缀自动机构出来然后就是要从根节点最快地转移到NULL并且保证字典序最小,结果空间炸得不要不要的。我伤心无奈之下忽然想起陈老师PPT里说SAM的边数不会超过状态数+字符串长度,于是我就把son[8]的指针数组改成了3*len的邻接链表(本来想写treap的,但考虑到空间开销……),时间多了个常数8而已。然而还是炸空间,后来看题解知道这就是个Hash?!
我们首先想答案会有多长。如果答案长度是7,那么长度为7的串就有
87=2097152
个,而原串中长度为7的子串顶多只有
1000000−7+1=999994
个,于是答案长度肯定不超过7。我们不妨枚举长度ans,将字符串长度为ans的子串的hash值用一个下标数组记录,最后遍历一边下标数组即可。时间复杂度
O(n)
。
时候我对拍了一下SAM+邻接链表的程序和Hash的程序,发现没什么问题,只不过前者的常数和空间都比较大而已。极限数据Hash要3s,而SAM+邻接链表要30s……
CODE(Hash):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1000010;
const int maxl=8;
bool vis[maxn];
int val[maxl];
int w[maxl];
int cur=0;
char s[maxn];
int t,slen;
void Print(int x,int len)
{
for (int i=1; i<=len; i++) w[++cur]=x&7,x>>=3;
while (cur) printf("%c",'A'+w[cur]),cur--;
printf("\n");
}
int main()
{
freopen("4.in","r",stdin);
freopen("4.out","w",stdout);
val[0]=1;
for (int i=1; i<maxl; i++) val[i]=val[i-1]<<3;
scanf("%d",&t);
while (t--)
{
scanf("%s",&s);
slen=strlen(s);
for (int ans=1; ans<=maxl; ans++)
{
memset(vis,false,sizeof(vis));
int Hash=0;
for (int i=1; i<=ans; i++) Hash=(Hash<<3)|(s[i-1]-'A');
for (int i=1; i<=slen-ans+1; i++)
{
vis[Hash]=true;
Hash-=((s[i-1]-'A')*val[ans-1]);
Hash=(Hash<<3)|(s[i+ans-1]-'A');
}
bool sol=false;
for (int i=0; i<val[ans]; i++)
if (!vis[i])
{
Print(i,ans);
sol=true;
break;
}
if (sol) break;
}
}
return 0;
}
CODE(SAM+邻接链表):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=1000010;
const int maxc=8;
struct State;
struct edge
{
int son;
State *obj;
edge *Next;
} e[maxn*3];
int num;
struct State
{
int val,id,len;
edge *head;
State *parent;
} SAM[maxn<<1];
State *Root,*last;
int cur;
int cnt[maxn];
State *Rank[maxn<<1];
char s[maxn];
int T,slen;
State *New_state(int v)
{
cur++;
SAM[cur].val=v;
SAM[cur].head=NULL;
SAM[cur].parent=NULL;
return SAM+cur;
}
State *Get(State *P,int x)
{
for (edge *p=P->head; p; p=p->Next)
if (p->son==x) return p->obj;
return NULL;
}
void Add(State *P,int x,State *Q)
{
if (!Q) return;
num++;
e[num].son=x;
e[num].obj=Q;
e[num].Next=P->head;
P->head=e+num;
}
void Build(int x)
{
int to=s[x]-'A';
x++;
State *P=last,*NP=New_state(x);
last=NP;
while ( P && !Get(P,to) ) Add(P,to,NP),P=P->parent;
if (!P)
{
NP->parent=Root;
return;
}
State *Q=Get(P,to);
if (P->val+1==Q->val) NP->parent=Q;
else
{
State *NQ=New_state(P->val+1);
for (int i=0; i<maxc; i++) Add(NQ,i, Get(Q,i) );
NQ->parent=Q->parent;
NP->parent=Q->parent=NQ;
while (P)
{
State *tag=Get(P,to);
if (tag!=Q) break;
for (edge *p=P->head; p; p=p->Next)
if (p->son==to)
{
p->obj=NQ;
break;
}
P=P->parent;
}
}
}
int main()
{
freopen("4.in","r",stdin);
freopen("4.out","w",stdout);
scanf("%d",&T);
while (T--)
{
scanf("%s",&s);
slen=strlen(s);
num=cur=-1;
last=Root=New_state(0);
for (int i=0; i<slen; i++) Build(i);
for (int i=0; i<=slen; i++) cnt[i]=0;
for (int i=0; i<=cur; i++) cnt[ SAM[i].val ]++;
for (int i=1; i<=slen; i++) cnt[i]+=cnt[i-1];
for (int i=0; i<=cur; i++) Rank[ --cnt[ SAM[i].val ] ]=SAM+i;
for (int i=cur; i>=0; i--)
{
Rank[i]->id=0;
State *tag=Get(Rank[i],0);
Rank[i]->len=(tag? tag->len:0)+1;
for (int j=1; j<maxc; j++)
{
tag=Get(Rank[i],j);
int temp=(tag? tag->len:0)+1;
if (temp<Rank[i]->len) Rank[i]->len=temp,Rank[i]->id=j;
}
}
State *P=Root;
while (P) printf("%c",'A'+P->id),P=Get(P,P->id);
printf("\n");
}
//printf("%d\n",sizeof(e)/1024);
//printf("%d\n",sizeof(SAM)/1024);
return 0;
}