====== 2019 ICPC Asia Taipei-Hsinchu Regional ======
[[http://codeforces.com/gym/102460|比赛网址]]
===== 训练详情 =====
* 时间:2020-5-17 13:00~18:00
* rank:''%%93/506%%''
* 完成情况:''%%7/10/13%%''
===== 题解 =====
===== A Rush Hour Puzzle =====
=== 题意 ===
一个大家都玩过的游戏,求步数
=== 题解 ===
solved by fyh
把6*6哈希压成一个状态,然后直接bfs暴力搜 代码写的过于丑,但是基本都是复制粘贴的。
#include
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef unsigned long long ULL;
typedef pair PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct State
{
int st[10][10];
State() {mem(st,0);}
ULL Hash()
{
ULL res=0;
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
res=(res+st[i][j])*107;
return res;
}
};
map vis;
map dis;
queue Q;
int main()
{
State init;
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
init.st[i][j]=read();
Q.push(init);
ULL hinit=init.Hash();
vis[hinit]=1;
dis[hinit]=0;
while(Q.size())
{
State now=Q.front();Q.pop();
ULL hnow=now.Hash();
if(dis[hnow]>8)return puts("-1"),0;
if(now.st[2][5]==1 && now.st[2][4]==1)return printf("%d\n",dis[hnow]+2),0;
for(int i=0;i<6;i++)
for(int j=0;j<6;j++)
{
if(now.st[i][j] && j+1<6 && now.st[i][j]==now.st[i][j+1] && (now.st[i][j+1]!=now.st[i][j+2] || j+2>=6) && (now.st[i][j-1]!=now.st[i][j] || j<=0))//横2车
{
if(j>0 && !now.st[i][j-1])//左移
{
State tmp=now;
tmp.st[i][j-1]=now.st[i][j];
tmp.st[i][j+1]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}
if(j+2<6 && !now.st[i][j+2])
{
State tmp=now;
tmp.st[i][j+2]=now.st[i][j];
tmp.st[i][j]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}//右移
}
else if(now.st[i][j] && j+2<6 && now.st[i][j]==now.st[i][j+1] && now.st[i][j+1]==now.st[i][j+2])//横3车
{
if(j>0 && !now.st[i][j-1])//左移
{
State tmp=now;
tmp.st[i][j-1]=now.st[i][j];
tmp.st[i][j+2]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}
if(j+3<6 && !now.st[i][j+3])
{
State tmp=now;
tmp.st[i][j+3]=now.st[i][j];
tmp.st[i][j]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}//右移
}
else if(now.st[i][j] && i+1<6 && now.st[i][j]==now.st[i+1][j] && (now.st[i+1][j]!=now.st[i+2][j] || i+2>=6) && (now.st[i-1][j]!=now.st[i][j] || i<=0))//竖2车
{
if(i>0 && !now.st[i-1][j])//上移
{
State tmp=now;
tmp.st[i-1][j]=now.st[i][j];
tmp.st[i+1][j]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}
if(i+2<6 && !now.st[i+2][j])
{
State tmp=now;
tmp.st[i+2][j]=now.st[i][j];
tmp.st[i][j]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}//右移
}
else if(now.st[i][j] && i+2<6 && now.st[i][j]==now.st[i+1][j] && now.st[i+1][j]==now.st[i+2][j])//竖3车
{
if(i>0 && !now.st[i-1][j])//上移
{
State tmp=now;
tmp.st[i-1][j]=now.st[i][j];
tmp.st[i+2][j]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}
if(i+3<6 && !now.st[i+3][j])
{
State tmp=now;
tmp.st[i+3][j]=now.st[i][j];
tmp.st[i][j]=0;
ULL htmp=tmp.Hash();
if(!vis[htmp])
{
dis[htmp]=dis[hnow]+1;
vis[htmp]=1;
Q.push(tmp);
}
}//右移
}
}
}
puts("-1");
return 0;
}
\\
===== B The Power Monitor System =====
=== 题意 ===
一棵树$n\leq10^5$,初始点和边都是白的,目标是将所有的边和点都染黑。操作代价为1,可以将一个节点和节点的所有出边全部染黑,服从以下三个规则:
* rule1:如果一条边被染黑,两个端点也被染黑。
* rule2:如果一条边两个点都被染黑,该边被染黑。
* rule3:一个点的度数$k>1$且$k-1$条边都被染黑,则剩下一条也被染黑
=== 题解 ===
补题 by fyh
很可怕的一个树形DP
设计状态$dp[u][0/1/2][0/1]$,第二维度的$0$表示该点由儿子传递过来,$1$表示自己染,$2$表示由父亲传递过来。第三维度的$0$表示不使用规则3进行染色,$1$表示使用规则3进行染色。
那么就是转移了
$dp[u][1][0]=\sum min(dp[v][0/1/2][0/1])$ 这个转移很显然,这个点都染上色了,子节点就可以xjb选了
$dp[u][1][1]$显然离谱,我们就不管他了
$dp[u][2][0]=\sum dp[v][0][1]$ 不通过规则3染色,故要所有的儿子都是通过规则3生成的,否则$(u,v)$该条边会被染黑
$dp[u][2][1]$为在$dp[u][2][0]$下选择一颗子树,将其替换成$min\{dp[v][2][0/1]\}$,表示$(u,v)$这条边是由规则3生成染黑的。
$dp[u][0][0]$ 为至少有一棵子树为$dp[v][1/0][0]$,(前者是儿子直接染黑,后者是儿子被染黑,而且儿子除了父边之外的所有边都是$0$,通过规则3使得$u$被染上的。)+其他子树取$min(00,01,10)$
$dp[u][0][1]$为在$dp[u][0][0]$ 下的其他子树中选择一颗子树,将其替换成$min\{dp[v][2][0/1]\}$。
就是感觉这个DP在第三维度定义的很玄学,于是我们的zzh鸽鸽就想到了把状态放在边上的做法…真的太强了。
#include
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=100010;
vector G[maxn];
int n,u,v,dp[maxn][3][2];
void dfs(int now,int fa)
{
for(int i=0;i<3;i++)for(int j=0;j<2;j++)dp[now][i][j]=maxn;
dp[now][1][0]=1;dp[now][2][0]=0;
if(G[now].size()==1 && fa)return;
dp[now][0][0]=dp[now][0][1]=dp[now][2][0]=dp[now][2][1]=0;
int choice[2],tp1[2][2]={0,maxn,maxn,maxn};//choice[]
choice[0]=0;choice[1]=maxn;
for(auto &v: G[now])
{
if(v==fa)continue;
dfs(v,now);
int tmp=min(dp[v][1][0],min(dp[v][0][0],dp[v][0][1]));
choice[1]=min(choice[1]+tmp,choice[0]+min(dp[v][0][0],dp[v][1][0])),choice[0]+=tmp;
tp1[1][1]=min(tp1[1][1]+tmp,min(tp1[0][1]+min(dp[v][2][0],dp[v][2][1]),tp1[1][0]+min(dp[v][0][0],dp[v][1][0])));
tp1[0][1]=min(tp1[0][1]+tmp,tp1[0][0]+min(dp[v][0][0],dp[v][1][0]));
tp1[1][0]=min(tp1[1][0]+tmp,tp1[0][0]+min(dp[v][2][0],dp[v][2][1]));
tp1[0][0]+=tmp;
dp[now][1][0]+=min(min(dp[v][0][0],dp[v][0][1]),min(dp[v][1][0],min(dp[v][2][0],dp[v][2][1])));
dp[now][2][1]=min(dp[now][2][0]+min(dp[v][2][0],dp[v][2][1]),dp[now][2][1]+dp[v][0][1]);
dp[now][2][0]+=dp[v][0][1];
}
dp[now][0][0]=choice[1],dp[now][0][1]=tp1[1][1];
}
int main()
{
n=read();
for(int i=1;i
\\
===== C Are They All Integers? =====
solved by hxm
=== 题意 ===
大水题
=== 题解 ===
略
===== D Tapioka =====
solved by wxg
=== 题意 ===
删掉一个字符串序列中的指定的字符串
=== 题解 ===
无敌水题,略
===== E The League of Sequence Designers =====
solved by wxg&hxm
=== 题意 ===
给定一个数字序列,求最大的连续和乘以长度。
现在给出一个错误的做法,错误做法仅计算最大连续和来更新答案
求构造出一个长度大于$l$小于$2000$的序列,使得错误答案和正确答案相差正好为$k$
=== 题解 ===
分析发现,只要在最大连续和的串前加上一个负数,那么这个做法就会出错,现在要构造出两种答案相差$k$
由于最大长度为$1999$,不妨就构造长度为$1999$的序列,第一位为$-1$,剩下为非负数,和为$a$,则有
$(a - 1) \times 1999 - a \times 1998 = k$
即$a = k + 1999$,算出$a$后,分配给剩下每一位即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
\\
===== H Mining a =====
solved by fyh&hxm
=== 题意 ===
$\frac{1}{n} = \frac{1}{a Xor b} + \frac{1}{b}$
给定$n$,$b$是任意的,求最大的$a$使等式成立
=== 题解 ===
化简得$b = n + \frac{n^2}{a Xor b - n}$
枚举分母即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
\\
===== J Automatic Control Machine =====
solved by hxm
=== 题意 ===
给定若干个集合,使用最少的集合并成全集
=== 题解 ===
bitset状压dp一下就好了
\\
===== K Automatic Control Machine =====
solved by wxg
=== 题意 ===
合并果子,数据还特小,题解略
===== J Largest Quadrilateral =====
=== 题意 ===
求平面点组成的最大四边形面积,$n\leq5000$
=== 题解 ===
补题by fyh
求凸包后先枚举对角线,然后通过旋转卡壳把剩下两个点找到,复杂度$O(n^2)$
当求出凸包只有三个点的时候,所得四边形是一个凹四边形。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=2010;
const double eps=1e-8;
struct Point{
double x,y;
Point() {}
Point(double _1,double _2):x(_1),y(_2) {}
Point operator - (const Point&s) const {return Point(x-s.x,y-s.y);}
double operator * (const Point&s) const {return x*s.y-y*s.x;}
double len() {return x*x+y*y;}
}a[maxn],p[maxn];
typedef Point Vec;
double dis(Point a,Point b){
return (a-b).len();
}
double S(Point a,Point b,Point c){
return abs((b-a)*(c-a));
}
bool judgeOnLeft(int p0,int p1,int p2){
double s=(a[p1]-a[p0])*(a[p2]-a[p0]);
return s<0 || (s==0 && dis(a[p1],a[p0])>=dis(a[p2],a[p0]));
}
bool cmp(Point p0,Point p1){
double s=(p0-a[1])*(p1-a[1]);
return s<0 || (s==0 && dis(p0,a[1])>=dis(p1,a[1]));
}
int n,top,sta[maxn];
double ans;
int Graham()
{
int temp=1,m=0;
for(int i=2;i<=n;i++)
if(a[i].x1 && judgeOnLeft(sta[top-1],i,sta[top]))top--;
sta[++top]=i;
}
for(int i=1;i<=top;i++)p[m++]=a[sta[i]];//,printf("%lf %lf\n",p[i-1].x,p[i-1].y);
return m-1;
}
double solve()
{
if(n<4)return 0.000;
double ans=0;
for(int i=0;i-eps)k=(k+1)%n;
while(S(p[i],p[j],p[(l+1)%n])-S(p[i],p[j],p[l])>-eps)l=(l+1)%n;
ans=max(ans,S(p[i],p[j],p[k])+S(p[i],p[j],p[l]));
// printf("%d %d %d %d\n",i,j,k,l);
}
}
return ans/2.0;
}
int main()
{
n=read();
if(n<3)return puts("0.000"),0;
for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x,&a[i].y);
n=Graham();
printf("%.3lf\n",solve());
}
===== 训练实况 =====
0~15min hxm,wxg横扫**K,C,D**三个签到题
15min~1h20min 口糊出**A**,fyh开写**A**,wxg,hxm讨论**E**,并讨论出来
1h20min~1h28min **A** 本机编译错误,哈希出现问题 hxm开**E**,并A
1h28min~1h??min 想出**H**,写**H** 并A
2h~2h14min hxmA**J**,其他人在读题
2h14min~2h36min fyhA**A**,
2h36min~ 读完所有题并确认全不可做,结束比赛
===== 训练总结 =====
像**A**这种代码量较大的题要尽量往后放,否则会增加罚时
尽早放弃
===== 改进 =====