一、哲哲回家
【题目描述】:
哲哲放学了,准备坐公交车回家。因为急着回家看MSN战士,所以他想用最短的时间回家,但是因为学校离家里比较远,有时候他需要转几趟车才可以回家。等车是需要一段时间的,而且每辆公交车的速度不一样,哲哲晕掉了,不知道怎样才可以在最短的时间里回家,作为一名cjoier,你应该帮帮他。
现在一直哲哲所在的城市有N个公交站点(学校在一号站点,哲哲家在N号站点)和M条公交线路,每条公交线路是一个有序集合,公交线路i包含Pi个站点a[i,1],a[i,2]…a[i,Pi],表示i号公交车从a[i,1]出发,经过a[i,2],a[i,3]…a[i,Pi]又重新回到a[i,1]。
Ti,Ri分别表示等第i号公交车的时间和第i号公交车经过一站的时间(如果你当前处在第i条公交线路上,你可以花Ti的时间等到这辆车,然后这辆车会花Ri的时间经过一站,下车时间忽略不计)。
【输入】:
第一行两个数N,M,表示公交站点数和公交线路数。
以下M行,第i+1行每行前三个数为Ti,Ri,Pi,然后接着有Pi个数,第j+3个数表示a[i,j]。
【输出】:
仅一行,表示所需的最少时间。
【样例输入】:
3 3
3 3 3 1 2 3
10 1 2 1 3
1 1 2 3 2
【样例输出】:
8
【说明】:
哲哲从学校(1)出发坐1号公交车在2号站下,再从2号站转3号车到家。
20%的数据N,M≤10;
60%的数据N,M≤100;
100%的数据N,M≤500;Pi≤30。
这实在是一道大水题,连边后跑一边最短路即可。连边的方式有两种:把每一条公交线路上的点两两连边,或把每个点拆成m个点,表示做哪一条线路过来。我写的第二种。代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<queue> 7 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 8 #define maxn 100010 9 #define INF (1LL<<50) 10 #ifdef WIN32 11 #define OT "%I64d" 12 #else 13 #define OT "%lld" 14 #endif 15 16 using namespace std; 17 typedef long long llg; 18 19 struct data{ 20 int now,from; 21 llg dis; 22 data(int a=0,int b=0,llg c=0):now(a),from(b),dis(c){}; 23 bool operator < (const data &h)const {return dis>h.dis;} 24 }k; 25 int n,m,ti[510],ri[510]; llg f[510][510],ans; 26 int head[maxn],next[maxn],to[maxn],c[maxn],tt; 27 priority_queue<data>q; 28 29 int getint(){ 30 int w=0,q=0; 31 char c=getchar(); 32 while((c<‘0‘||c>‘9‘)&&c!=‘-‘) c=getchar(); 33 if(c==‘-‘) q=1,c=getchar(); 34 while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); 35 return q?-w:w; 36 } 37 38 void link(int x,int y,int z){ 39 to[++tt]=y;next[tt]=head[x];head[x]=tt; 40 //to[++tt]=x;next[tt]=head[y];head[y]=tt; 41 //c[tt]=c[tt-1]=z; 42 c[tt]=z; 43 } 44 45 void Dijstra(){ 46 for(int i=0;i<=n;i++) 47 for(int j=0;j<=m;j++) f[i][j]=INF; 48 f[1][0]=0; 49 q.push(data(1,0,0)); 50 while(!q.empty()){ 51 k=q.top();q.pop(); 52 int u=k.now,xx=k.dis,ff=k.from; 53 if(xx==f[u][ff]) 54 for(int i=head[u],v;v=to[i],i;i=next[i]){ 55 int jisuan=ti[c[i]]+ri[c[i]]; 56 if(c[i]==ff) jisuan-=ti[c[i]]; 57 if(f[v][c[i]]>xx+jisuan){ 58 f[v][c[i]]=xx+jisuan; 59 q.push(data(v,c[i],f[v][c[i]])); 60 } 61 } 62 } 63 for(int i=0;i<=m;i++) ans=min(ans,f[n][i]); 64 } 65 66 int main(){ 67 File("home"); 68 n=getint();m=getint(); 69 for(int i=1,x,la,now,ll;i<=m;i++){ 70 ti[i]=getint();ri[i]=getint(); 71 x=getint(); if(x) ll=la=getint(); 72 for(int j=1;j<x;j++){ 73 now=getint(); 74 link(la,now,i); 75 la=now; 76 } 77 if(x) link(now,ll,i); 78 } 79 ans=INF;Dijstra(); 80 printf(OT,ans); 81 fclose(stdin);fclose(stdout); 82 return 0; 83 }
二、旋转单词阵
【题目描述】:
背英语单词对于LD同学来说是一件巨大的工程,需要经过七七四十九遍背诵加九九八十一遍默写这单词才记得下来。LD想发明一种新的记忆方法使得他可以更加快速的记忆单词,他发现了一个有趣的事情,某一个长度为len单词旋转之后生成的新的len-1个单词,在把他们按字典序排序,形成的新矩阵会有很奇特的性质,比如code
code
odec
deco
ecod
再把他们按字典序排序:
code
deco
ecod
odec
可以发现最后一列的单词为eodc,LD在想,能不能只记下最后一列单词就可以还原出整个单词矩阵呢?当然,因为这个矩阵中的单词有很多是无用的,所以他只要你输出第一行就可以了(也就是字典序最小的单词)。
【输入】:
仅一行,表示最后一列的单词(为小写字母构成)。
【输出】:
仅一行,表示第一行的单词。
【样例输入】:
eodc
【样例输出】:
code
【说明】:
样例说明见题目描述。
20%的数据保证单词长度≤10;
60%的数据保证单词长度≤500;
100%的数据保证单词长度≤300000;
这道题真是太神辣!IOI2001的预备题啊!不过为什么标解这么简单啊!
我们可以将得到的单词排个序,不难发现我们就得到了每一个字母之后对应着那个字母。但重复的字母怎么办呢?我们排序时可以考虑加入第二关键字,即这个字母是第几次出现。因为若单词首字母相同,那么我们就可以根据我们得到的字母的顺序来确定先选哪一个了。自己想一想就可以明白了。我考场上为什么没有想出来啊!代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 8 #define maxn 300010 9 10 using namespace std; 11 typedef long long llg; 12 13 struct data{ 14 int x,b,bb; 15 data(int a=0,int b=0):x(a),b(b){} 16 bool operator < (const data &h)const{ 17 if(x==h.x) return b<h.b; 18 return x<h.x; 19 } 20 }a1[maxn],a2[maxn]; 21 char s[maxn]; 22 int n=1,a[31]; 23 24 int main(){ 25 File("word"); 26 while(scanf("%c",&s[n])!=EOF){ 27 a1[n].x=s[n]-‘a‘; a1[n].bb=n; 28 a1[n].b=++a[a1[n].x]; 29 a2[n]=a1[n]; n++; 30 } 31 n--; sort(a2+1,a2+n+1); 32 for(int i=1,nn=1;i<=n;i++,nn=a2[nn].bb) 33 printf("%c",a2[nn].x+‘a‘); 34 fclose(stdin);fclose(stdout); 35 return 0; 36 }
三、切蛋糕
【题目描述】:
小J因为考试full mark被奖励了一个N*M的蛋糕,现在他想和大家分享这一个蛋糕。
划分的方法如下:每次选出已经分出的某一个矩形蛋糕将其一刀划分成两个小矩形,当然,这一刀可以选择横切或者竖切,最后被分成N*M块1*1的小蛋糕。
他希望统计出一共有多少种不同的划分方案,两个划分方案不同被定义为:其中任意一个划分方案的某一条刀痕在另一个划分方案中不存在。
【输入】:
仅一行两个数N,M。
【输出】:
仅一个数为方案数 mod 1024。
【样例输入】:
3 2
【样例输出】:
4
【说明】:
40%的数据N,M≤15;
100%的数据N,M≤300;
这道题我们考虑dp做。不难想到用f[i][j]表示i行j列的矩形切割方案数。但是这样直接转移的话会有重复。于是我们加一维状态[0,1,2],0表示这一刀横竖都可以,1表示竖切,2表示横切,于是就可以转移了。横切一刀后上面那一块下一刀不可以横切,竖切类似。代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<ctime> 7 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 8 #define mod 1024 9 10 using namespace std; 11 12 int n,m,f[310][310][3]; 13 14 int main(){ 15 File("cake"); 16 scanf("%d %d",&n,&m); 17 f[1][1][0]=f[1][1][1]=f[1][1][2]=1; 18 for(int x=1,now;x<=n;x++) 19 for(int y=1;y<=m;y++){ 20 for(int i=1;i<x;i++){ 21 now=f[i][y][2]*f[x-i][y][0]; 22 f[x][y][0]+=now; f[x][y][1]+=now; 23 f[x][y][0]%=mod; f[x][y][1]%=mod; 24 } 25 for(int i=1;i<y;i++){ 26 now=f[x][i][1]*f[x][y-i][0]; 27 f[x][y][0]+=now; f[x][y][2]+=now; 28 f[x][y][0]%=mod; f[x][y][2]%=mod; 29 } 30 } 31 printf("%d",f[n][m][0]); 32 fclose(stdin);fclose(stdout); 33 return 0; 34 }
四、套圈游戏
【题目描述】:
Ly来到了一个游乐园,他刚刚进入就发现了一个他最喜欢玩的游戏,套圈游戏!
和很多普通的套圈游戏一样,在一个平面上有很多价值不同的礼品,当然ly希望用一个圈套住价值总尽量大的礼品。不过这个“圈”很特殊,是一个直角三角形,而且直角必须落在某个礼品上,现在给出你这个直角三角形的形状和礼品的位置和价值,问你怎样才可以套住总价值最大的礼品。
【输入】:
第一行一个整数N,表示有N个礼品。
第二行两个整数A,B,表示这个直角三角形的两个直角边的长度(A为平行于X轴的直角边的长度,B为平行于Y轴的直角边的长度),这个直角三角形不允许旋转,也就是说直角在最左下角。
接下来N行,第i+2行有三个整数Xi,Yi,Wi,表示礼品i的坐标(Xi,Yi)和价值Wi。
【输出】:
仅一个数,表示最大价值。
【样例输入】:
4
5 5
1 7 100
1 1 2
2 2 99
7 1 100
【样例输出】:
101
【说明】:
40%的数据保证N≤5000;
100%的数据保证N≤100000;
100%的数据保证0<Xi,Yi≤10000,-10000<Wi<10000。
容许我先%%%XYK大爷(这道题唯一的AC者)
这道题用的方法很巧妙。考虑一个点B在以一个点A为左下角的三角形中需要满足什么条件。我们可以设直线l为那个直角三角形斜边过原点时的直线。当B到l的距离减去A到l的距离小于这个直角三角形的斜边上的高时可以计算(当B的x坐标与y坐标均>=A时)。于是我们可以把点按到l的距离从远到近排一遍序,然后不断加点、删点,用树状数组维护一下x轴、y轴的前缀和即可。代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 7 #define maxn 100010 8 9 using namespace std; 10 typedef long long llg; 11 12 int cx[maxn/10],cy[maxn/10]; 13 int n,_x,_y,A,B,ans,ll; 14 struct data{ 15 int x,y,z,d; 16 bool operator < (const data &h)const{ 17 return d<h.d; 18 } 19 }a[maxn]; 20 21 int getint(){ 22 int w=0;bool q=0; 23 char c=getchar(); 24 while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar(); 25 if(c==‘-‘) q=1,c=getchar(); 26 while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); 27 return q?-w:w; 28 } 29 30 void add(int x,int y,int z){ 31 while(x<=_x) cx[x]+=z,x+=x&(-x); 32 while(y<=_y) cy[y]+=z,y+=y&(-y); 33 } 34 35 int sum(int x,int y){ 36 int t=0; 37 while(x) t+=cx[x],x-=x&(-x); 38 while(y) t+=cy[y],y-=y&(-y); 39 return t; 40 } 41 42 int main(){ 43 File("circle"); 44 n=getint();A=getint();B=getint(); 45 for(int i=1;i<=n;i++){ 46 _x=max(a[i].x=getint(),_x); 47 _y=max(a[i].y=getint(),_y); 48 a[i].z=getint(); 49 a[i].d=B*a[i].x+A*a[i].y; 50 } 51 sort(a+1,a+n+1); 52 for(int i=n,la=n;i;i--){ 53 int now=a[i].z; 54 while(a[la].d-a[i].d>A*B){ 55 add(a[la].x,a[la].y,-a[la].z); 56 ll-=a[la].z; la--; 57 } 58 now+=ll-sum(a[i].x-1,a[i].y-1); 59 ans=max(ans,now); ll+=a[i].z; 60 add(a[i].x,a[i].y,a[i].z); 61 } 62 printf("%d",ans); 63 return 0; 64 }