训练感想
照惯例,先膜拜两位大神队友 @tomriddly@jiefangxuanyan
今天是World Final
的日子。。训练的时候老想着看直播。。状态不佳。。
不过还好,最后把卡住的题都写出来了,值得总结一下。
题目分析
A题 Continued Fraction
题意
给定连分数的定义,输入两个连分数,输出这两个分数的四则运算结果的连分数形式。
直接上分数类,先把一个连分数转化成一个正常分数,计算之后再转换回连分数。
这道题要注意,计算差的时候可能是负数 ,这时候把a0处理成负数就可以了。
/* written by jiefangxuanyan */
#include <cstdio>
#include <algorithm>
typedef long long LL;
LL gcd(LL a,LL b){
return a?gcd(b%a,a):b;
}
struct fr{
LL a,b;
fr(LL aa,LL bb){
LL g=gcd(aa,bb);
a=aa/g;
b=bb/g;
}
fr operator+(const fr &x){
return fr(a*x.b+x.a*b,b*x.b);
}
fr operator-(const fr &x){
return fr(a*x.b-x.a*b,b*x.b);
}
fr operator*(const fr &x){
return fr(a*x.a,b*x.b);
}
fr operator/(const fr &x){
return fr(a*x.b,b*x.a);
}
void add(LL x){
a+=b*x;
}
LL split(){
LL r=a/b;
a%=b;
if(a<0){
r--;
a+=b;
}
return r;
}
void inv(){
std::swap(a,b);
if(b<0){
a=-a;
b=-b;
}
}
operator bool(){
return a;
}
};
const int N=20;
LL in[N];
int main(){
int cn=0;
while(true){
int n,m;
scanf("%d%d",&n,&m);
if(!(n|m)){
return 0;
}
for(int i=0;i<n;i++){
scanf("%I64d",in+i);
}
fr x(0,1);
for(int i=n-1;i>=0;i--){
x.add(in[i]);
x.inv();
}
x.inv();
for(int i=0;i<m;i++){
scanf("%I64d",in+i);
}
fr y(0,1);
for(int i=m-1;i>=0;i--){
y.add(in[i]);
y.inv();
}
y.inv();
fr add=x+y,sub=x-y,mul=x*y,div=x/y;
printf("Case %d:\n",++cn);
add.inv();
while(add){
add.inv();
printf("%I64d",add.split());
if(add){
putchar(' ');
}
}
putchar('\n');
sub.inv();
while(sub){
sub.inv();
printf("%I64d",sub.split());
if(sub){
putchar(' ');
}
}
putchar('\n');
mul.inv();
while(mul){
mul.inv();
printf("%I64d",mul.split());
if(mul){
putchar(' ');
}
}
putchar('\n');
div.inv();
while(div){
div.inv();
printf("%I64d",div.split());
if(div){
putchar(' ');
}
}
putchar('\n');
}
}
B题 A Cure for the Common Code
题意
给一个字符串,大概是abcbcbcbca
这种,我们可以把他变成a4(bc)a
这样。前面的字符串长度是11,而后面的只有7(算上括号),问给定一个字符串,能把他变成的最短长度是多少。
用t[l][r]
记录l到r之间的字符串所能折叠的最短长度,然后进行记忆化搜索。状态转移的时候要枚举两部分,一部分是只把字符串分成两部分,另外一部分是要把字符串中的循环节找出来进行更新。
/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=550;
int t[N][N];
char in[N];
int numLen(int x){
int s=0;
while(x){
x/=10;
s++;
}
return s;
}
int dfs(int l,int r){
int len=r-l;
if(len<=1){
return len;
}
if(t[l][r]!=-1){
return t[l][r];
}
int s=0x3fffffff;
for(int m=l+1;m<r;m++){
s=std::min(dfs(l,m)+dfs(m,r),s);
}
for(int lp=1;lp<len;lp++){
if(len%lp==0){
int seg=len/lp;
for(int begin=l;begin<l+lp;begin++){
for(int st=1;st<seg;st++){
if(in[begin]!=in[begin+st*lp]){
goto lblNoneLoop;
}
}
}
s=std::min(dfs(l,l+lp)+(lp>1?2:0)+numLen(seg),s);
lblNoneLoop:
;
}
}
//printf("%d,%d:%d\n",l,r,s);
t[l][r]=s;
return s;
}
int main(){
int cn=0;
while(true){
gets(in);
if(strcmp(in,"0")==0){
return 0;
}
int n=strlen(in);
memset(t,-1,sizeof(t));
printf("Case %d: %d\n",++cn,dfs(0,n));
}
}
C题 Domiyahtzee!
题意
找安神的意思,应该是一道大型(中型)模拟,但是写到一半没写完。
等写出来了再看看怎么回事吧。。。
D题 Generalized Roman Numerals
题意
给定一个罗马数字的串的定义,定义是递归的。当左半串代表的数比右半串大的时候,串的值是左半加右半,反之,值为右半减左半。
比如IVX
可以为4,也可以为6
给定一个串,问能够代表哪些数字。
搜索并且记忆化。注意这里需要在每个状态下记录这个串的可能的数值。我们用一个vector<int>
去记录。刚开始的时候安神对这个容器进行排序去重,超时了。然后东神用了一个bool has[N][N][5500]
解决了这个问题。这里用hash
来判定的确要快一些,至于如何实现,我个人认为这种方式就很不错了。如果安神的unorder_set调试成功也是可以的。。
/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
const int N=55;
std::vector<int> t[N][N];
bool has[N][N][5500];
char in[N];
int ch2n(char ch){
return ch=='I'?1:ch=='V'?5:ch=='X'?10:ch=='L'?50:ch=='C'?100:0;
}
void dfs(int l,int r,std::vector<int> &vr){
if(!vr.empty()){
return;
}
if(r-l==1){
vr.push_back(ch2n(in[l]));
return;
}
for(int m=l+1;m<r;m++){
dfs(l,m,t[l][m]);
dfs(m,r,t[m][r]);
for(int i=0;i<t[l][m].size();i++){
for(int j=0;j<t[m][r].size();j++){
int a=t[l][m][i],b=t[m][r][j];
int cur=a<b?b-a:a+b;
if(!has[l][r][cur]){
has[l][r][cur]=true;
vr.push_back(cur);
}
}
}
}
}
int main(){
int cn=0;
while(true){
gets(in);
if(strcmp(in,"0")==0){
return 0;
}
int n=strlen(in);
for(int i=0;i<n;i++){
for(int j=i+1;j<=n;j++){
t[i][j].clear();
}
}
memset(has,0,sizeof(has));
dfs(0,n,t[0][n]);
printf("Case %d:",++cn);
std::sort(t[0][n].begin(),t[0][n].end());
for(int i=0;i<t[0][n].size();i++){
printf(" %d",t[0][n][i]);
}
putchar('\n');
}
}
E题 Inspectors
题意
没咋看。。俩队友就给过了。。。。求队友解释题意。。
貌似是二分图带权匹配 用了kuangbin的大模板
里的KM
敲模板一定要检查一遍!
/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
const int N=110;
const int INF=0x3f3f3f3f;
int nx,ny;
int g[N][N];
int linker[N],lx[N],ly[N];
int slack[N];
bool visx[N],visy[N];
bool DFS(int x){
visx[x]=true;
for(int y=0;y<ny;y++){
if(visy[y]){
continue;
}
int tmp=lx[x]+ly[y]-g[x][y];
if(tmp==0){
visy[y]=true;
if(linker[y]==-1||DFS(linker[y])){
linker[y]=x;
return true;
}
} else if(slack[y]>tmp){
slack[y]=tmp;
}
}
return false;
}
int KM(){
memset(linker,-1,sizeof(linker));
memset(ly,0,sizeof(ly));
for(int i=0;i<nx;i++){
lx[i]=-INF;
for(int j=0;j<ny;j++){
if(g[i][j]>lx[i]){
lx[i]=g[i][j];
}
}
}
for(int x=0;x<nx;x++){
for(int i=0;i<ny;i++){
slack[i]=INF;
}
while(true){
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(DFS(x)){
break;
}
int d=INF;
for(int i=0;i<ny;i++){
if(!visy[i]&&d>slack[i]){
d=slack[i];
}
}
for(int i=0;i<nx;i++){
if(visx[i]){
lx[i]-=d;
}
}
for(int i=0;i<ny;i++){
if(visy[i]){
ly[i]+=d;
} else{
slack[i]-=d;
}
}
}
}
int res=0;
for(int i=0;i<ny;i++){
if(linker[i]!=-1){
res+=g[linker[i]][i];
}
}
return res;
}
int main(){
int cn;
scanf("%d",&cn);
for(int ci=1;ci<=cn;ci++){
scanf("%d",&nx);
ny=nx;
memset(g,-0x3f,sizeof(g));
for(int i=0;i<nx;i++){
for(int j=i+1;j<ny;j++){
scanf("%d",g[i]+j);
g[i][j]=-g[i][j];
g[j][i]=g[i][j];
}
}
printf("Case %d: %d\n",ci,-KM());
}
return 0;
}
F题 Path of Least Persistence
题意
给一个n*m的地图,每个格子上面有一个方向和长度。只能按照约束走。要求改变一个格子上的方向和长度,使得从(1,1)
走到end
的跳跃次数最少。
也用了一个dp的思想,貌似有些细节问题。最后一分钟过的你敢信!
/* written by jiefangxuanyan */
#include <cstdio>
#include <cstring>
//#include <iostream>
#include <algorithm>
const int N=110;
int d[N][N];
int dx(char ch){
return ch=='N'?-1:ch=='S'?1:ch=='E'?0:0;
}
int dy(char ch){
return ch=='N'?0:ch=='S'?0:ch=='E'?1:-1;
}
char op[N][N];
int jmp[N][N];
int n,m;
bool valid(int x,int y){
return 0<=x&&x<n&&0<=y&&y<m;
}
int dfs(int x,int y){
if(!valid(x,y)){
return 0x3fffffff;
}
if(d[x][y]!=-1){
return d[x][y];
}
if(x==n-1&&y==m-1){
d[x][y]=0;
return 0;
}
d[x][y]=0x3fffffff;
d[x][y]=dfs(x+jmp[x][y]*dx(op[x][y]),y+jmp[x][y]*dy(op[x][y]))+1;
return d[x][y];
}
const int xx[]={-1,1,0,0},yy[]={0,0,-1,1};
int lastx,lasty,lastjmp;
char lastop;
struct J{
int d,x,y,jmp;
char op;
J(int d,int x,int y,int jmp,char op):d(d),x(x),y(y),jmp(jmp),op(op){}
bool operator<(const J &fuck)const{
if(d!=fuck.d){
return d<fuck.d;
}
if(op=='X'&&fuck.op=='X'){
return false;
}
if(op=='X'&&fuck.op!='X'||op!='X'&&fuck.op=='X'){
return op=='X';
}
if(x!=fuck.x){
return x<fuck.x;
}
if(y!=fuck.y){
return y<fuck.y;
}
if(jmp!=fuck.jmp){
return jmp<fuck.jmp;
}
return op<fuck.op;
}
};
bool dfs2vis[N][N];
J dfs2(int x,int y){
if(!valid(x,y)||dfs2vis[x][y]){
return J(0x3fffffff,0,0,0,'X');
}
dfs2vis[x][y]=true;
if(x==n-1&&y==m-1){
return J(0,0,0,0,'X');
}
J r(0x3fffffff,0,0,0,'X');
for(int dir=0;dir<4;dir++){
for(int st=1;valid(x+xx[dir]*st,y+yy[dir]*st);st++){
r=std::min(J(dfs(x+xx[dir]*st,y+yy[dir]*st)+1,x,y,st,"NSWE"[dir]),r);
}
}
J last=dfs2(x+jmp[x][y]*dx(op[x][y]),y+jmp[x][y]*dy(op[x][y]));
last.d++;
return std::min(last,r);
}
int main(){
int cn=0;
while(true){
scanf("%d%d",&n,&m);
if(!(n|m)){
return 0;
}
for(int x=0;x<n;x++){
for(int y=0;y<m;y++){
if(!(x==n-1&&y==m-1)){
scanf("%d%c",jmp[x]+y,op[x]+y);
}
}
}
memset(d,-1,sizeof(d));
memset(dfs2vis,0,sizeof(dfs2vis));
J r=dfs2(0,0);
printf("Case %d: ",++cn);
if(r.d>=0x3fffffff){
puts("impossible");
} else if(r.op=='X'){
printf("none %d\n",r.d);
} else {
printf("%d %d %d%c %d\n",r.x,r.y,r.jmp,r.op,r.d);
}
}
return 0;
}
G题 Speed Skills
题意
还不会、、先占个坑。
H题 Time Warp
题意
这题也坑了我们好久。。题意就不解释了,刚开始的时候东神寻思直接把表打出来然后再算,然后发现精度有很大的问题。。调了很久都没过,直接打乱了节奏。最后我想出了用追及问题
的想法来写。。我简直是个小学生。。。不过过题就好辣!
/*************************************************************************
> File Name: ph.cpp
> Author: znl1087
> Mail: loveCJNforever@gmail.com
> Created Time: 三 5/20 18:39:24 2015
************************************************************************/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
void print(int se){
se = (se +43200)%43200;
int h = se / 3600 +1;
se %= 3600;
int m = se / 60;
int s = se%60;
printf("%d:%02d:%02d\n",h,m,s);
}
int main(){
int ca;
cin>>ca;
int a,b;
char op[10];
for(int cas = 1 ; cas <= ca ;cas++){
scanf("%d %s %d",&a, op ,&b);
printf("Case %d: ",cas);
if( op[0] == 'a'){
int have = (12 - b) * 30;
double need = (double)(a > have? a - have:360 + a - have);
int ans = (int)round(need / (11.0/120.0));
print((b-1) * 3600 + ans);
}else if(op[0] == 't'){
int have = (12 - b) * 30;
double need = (double)(a < have? have -a: 360 + have -a);
int ans = (int)round(need / (11.0/120.0));
print((b-1) * 3600 - ans);
}
}
return 0;
}
I题 Watch, Man!
题意
不会,占坑待填。。貌似是个计算几何?