感冒了,同时心情极差,状态不好ww
Rank
A. Start
打磨你。
T1 放大模拟还是过于抽象了,开局劝退,遂倒序开题。
赛时想复杂了一点,开了两个二维数组存牌,同时存 double 状态时也出了问题,还没有考虑负数的向下取整。我太蒻了
改后虽然还是 300 多行,但是思路起码清晰了很多,改了几个小错就 A 了。
Code:
const int N=3e5+5;
const int mod=1e9+7;
int n,m,k,p,tot=1;
// tot 为牌堆摸取位置
string nam[6],have[6][4],paidui[N];
// 玩家名,玩家拥有手牌,牌堆
bool turnn,doubll,jp,endd;
// 分别表示出牌顺序,double 施加情况,解牌跳过 double,结束回合
// 由于 double 无论如何都是施加在下一个出牌的人身上的,所以一个变量就够
bool cmp(string a,string b)// 无 double 优先级排序
{
int anum=-1,bnum=-1;
if(a=="C2") anum=0;
else if(a=="A99") anum=1;
else if(a=="A49") anum=2;
else if(a=="A19") anum=3;
else if(a=="A9") anum=4;
else if(a=="A5") anum=5;
else if(a=="A2") anum=6;
else if(a=="A1") anum=7;
else if(a=="B1") anum=8;
else if(a=="B9") anum=9;
else if(a=="B19") anum=10;
else if(a=="D2") anum=11;
else if(a=="E99") anum=12;
else if(a=="E49") anum=13;
else if(a=="E0") anum=14;
else if(a=="PASS") anum=15;
else if(a=="TURN") anum=16;
else if(a=="DOUBLE") anum=17;
if(b=="C2") bnum=0;
else if(b=="A99") bnum=1;
else if(b=="A49") bnum=2;
else if(b=="A19") bnum=3;
else if(b=="A9") bnum=4;
else if(b=="A5") bnum=5;
else if(b=="A2") bnum=6;
else if(b=="A1") bnum=7;
else if(b=="B1") bnum=8;
else if(b=="B9") bnum=9;
else if(b=="B19") bnum=10;
else if(b=="D2") bnum=11;
else if(b=="E99") bnum=12;
else if(b=="E49") bnum=13;
else if(b=="E0") bnum=14;
else if(b=="PASS") bnum=15;
else if(b=="TURN") bnum=16;
else if(b=="DOUBLE") bnum=17;
return anum<bnum;
}
bool cmpd(string a,string b)// 有 double 优先级排序
{
int anum=-1,bnum=-1;
if(a=="PASS") anum=0;
else if(a=="TURN") anum=1;
else if(a=="DOUBLE") anum=2;
else if(a=="D2") anum=3;
else if(a=="B19") anum=4;
else if(a=="B9") anum=5;
else if(a=="B1") anum=6;
else if(a=="A1") anum=7;
else if(a=="A2") anum=8;
else if(a=="A5") anum=9;
else if(a=="A9") anum=10;
else if(a=="A19") anum=11;
else if(a=="A49") anum=12;
else if(a=="A99") anum=13;
else if(a=="C2") anum=14;
else if(a=="E0") anum=15;
else if(a=="E49") anum=16;
else if(a=="E99") anum=17;
if(b=="PASS") bnum=0;
else if(b=="TURN") bnum=1;
else if(b=="DOUBLE") bnum=2;
else if(b=="D2") bnum=3;
else if(b=="B19") bnum=4;
else if(b=="B9") bnum=5;
else if(b=="B1") bnum=6;
else if(b=="A1") bnum=7;
else if(b=="A2") bnum=8;
else if(b=="A5") bnum=9;
else if(b=="A9") bnum=10;
else if(b=="A19") bnum=11;
else if(b=="A49") bnum=12;
else if(b=="A99") bnum=13;
else if(b=="C2") bnum=14;
else if(b=="E0") bnum=15;
else if(b=="E49") bnum=16;
else if(b=="E99") bnum=17;
return anum<bnum;
}
// 排序保证选择普通牌顺序
namespace Wisadel
{
int Wgetnext(int now)
{// 找到下一个出牌的人
if(turnn) now=(now==1?n:now-1);
else now=(now==n?1:now+1);
return now;
}
void Wworkjp(int now)
{// 解牌优先
bool can=0;
sort(have[now]+1,have[now]+4,cmpd);
// 按 double 优先级排序
fo(i,1,3)
{// 先考虑解牌
string ss=have[now][i];
if(ss=="PASS")
{
cout<<"used "<<ss<<",now p="<<p<<".\n";
jp=1;can=1;// 直接跳过
have[now][i]=paidui[tot++];// 取牌
break;
}
else if(ss=="TURN")
{
cout<<"used "<<ss<<",now p="<<p<<".\n";
jp=1;can=1;
if(turnn) turnn=0;
else turnn=1;// 反转出牌顺序
have[now][i]=paidui[tot++];
break;
}
else if(ss=="DOUBLE")
{
cout<<"used "<<ss<<",now p="<<p<<".\n";
jp=1;can=1;
doubll=1;// 记录 double 效果
have[now][i]=paidui[tot++];
break;
}
}
if(can) return;
// 若无解牌,则考虑基本牌
int num=0,no,minn=1e9;
fo(i,1,3)
{// 考虑基本牌使用,按要求找最小值
string ss=have[now][i];no=p;
if(ss=="A1") no+=1;
else if(ss=="A2") no+=2;
else if(ss=="A5") no+=5;
else if(ss=="A9") no+=9;
else if(ss=="A19") no+=19;
else if(ss=="A49") no+=49;
else if(ss=="A99") no+=99;
else if(ss=="B1") no-=1;
else if(ss=="B9") no-=9;
else if(ss=="B19") no-=19;
else if(ss=="C2") no*=2;
else if(ss=="D2") no=floor(1.0*no/2);
else if(ss=="E0") no=0;
else if(ss=="E49") no=49;
else if(ss=="E99") no=99;
else no=10000;
if(no<=99&&no<minn) num=i,minn=no,can=1;
}
if(!can)
{// 仍必败
endd=1;
cout<<"lost the game.\n";
}
else
{// 可不败,打一摸一
doubll=0;
cout<<"used "<<have[now][num]<<",now p="<<minn<<".\n";
have[now][num]=paidui[tot++];
p=minn;
}
}
void Wwork(int now)
{// 普通牌优先
sort(have[now]+1,have[now]+4,cmp);
// 按无 double 优先级排序
bool can=0;int num=0,no,maxx=-1e9;
fo(i,1,3)
{// 考虑基本牌使用,按要求找范围内最大值
string ss=have[now][i];no=p;
if(ss=="A1") no+=1;
else if(ss=="A2") no+=2;
else if(ss=="A5") no+=5;
else if(ss=="A9") no+=9;
else if(ss=="A19") no+=19;
else if(ss=="A49") no+=49;
else if(ss=="A99") no+=99;
else if(ss=="B1") no-=1;
else if(ss=="B9") no-=9;
else if(ss=="B19") no-=19;
else if(ss=="C2") no*=2;
else if(ss=="D2") no=floor(1.0*no/2);
else if(ss=="E0") no=0;
else if(ss=="E49") no=49;
else if(ss=="E99") no=99;
else no=10000;
if(no<=99&&no>maxx) num=i,maxx=no,can=1;
}
if(can)
{// 能打则打一摸一
cout<<"used "<<have[now][num]<<",now p="<<maxx<<".\n";
p=maxx;
have[now][num]=paidui[tot++];
return;
}
fo(i,1,3)
{// 否则考虑解牌
string ss=have[now][i];
if(ss=="PASS")
{
cout<<"used "<<ss<<",now p="<<p<<".\n";
can=1;
have[now][i]=paidui[tot++];
break;
}
else if(ss=="TURN")
{
cout<<"used "<<ss<<",now p="<<p<<".\n";
can=1;
if(turnn) turnn=0;
else turnn=1;
have[now][i]=paidui[tot++];
break;
}
else if(ss=="DOUBLE")
{
cout<<"used "<<ss<<",now p="<<p<<".\n";
can=1;doubll=1;
have[now][i]=paidui[tot++];
break;
}// 过程中直接打一摸一
}
if(!can)
{// 必败
endd=1;
cout<<"lost the game.\n";
}
}
short main()
{
// freopen("A.in","r",stdin),freopen("1.out","w",stdout);
n=qr,m=qr,k=qr;
fo(i,1,n) cin>>nam[i]>>have[i][1]>>have[i][2]>>have[i][3];
fo(i,1,k) cin>>paidui[i];
// 输入
int laslos=1;
// 从上回合熟的人开始,第一回合从 1 号开始
fo(roundd,1,m)
{
printf("Round %d:\n",roundd);
doubll=p=turnn=endd=0;int now=laslos;
// 初始化标记变量
while(1)
{
cout<<nam[now]<<' ';jp=0;
if(doubll)
{// 有 double 效果
Wworkjp(now);
// 先按 double 打一次
if(jp)
{// 使用了解牌,直接跳过
now=Wgetnext(now);
continue;
}
else if(endd)
{// 败了
laslos=now;
if(roundd!=m)
{// 重新摸牌
have[now][1]=paidui[tot++];
have[now][2]=paidui[tot++];
have[now][3]=paidui[tot++];
}
break;
}
else
{// 否则按无 double 效果再进行一回合
cout<<nam[now]<<' ';
Wwork(now);
}
}
else Wwork(now);
// 无 double 效果
if(endd)
{// 败了
laslos=now;
if(roundd!=m)
{
have[now][1]=paidui[tot++];
have[now][2]=paidui[tot++];
have[now][3]=paidui[tot++];
}
break;
}
now=Wgetnext(now);
}
}
return Ratio;
}
}
int main(){return Wisadel::main();}
确实又臭又长了属于是。
咕一个长度在 150 行以内的,不无脑压行。
咕
B. Mine
一个简单的 dp,但赛时倒数开的,时间不是很足,在暴搜上耽误很久。
所以以后看见题不能光想着搜了,脑子浑了就打醒之后考虑考虑 dp。
思路挺简单,讲的也很明白,不多写了。
C. 小凯的疑惑
数论题。同样最后开的这道题,压根没去想正解,要不是打 Subtask1 时候暴力摸出来了 Subtask2,这题就保龄了。
其实不互质无解挺显然的,但赛时没看出来,唐了。
然后远古的题考察化简式子化成 \(O(1)\) 的,但由于评测机的更新换代现在 \(10^8\) 的范围 \(O(n)\) 也能过,但还是没想出来。
粘一张推导,以后看。
D. 春节十二响
。
。
其实一眼泄题了不是吗
一道启发式合并。
题面挺简单的,起码看了 T1 之后这么想。
首先想的是树剖,每条链按类似田忌赛马的思想,最大值放在一起,次大值放在一起,以此类推,答案即为每一堆中最大值的和。
那么发现它是一种合并的思想,最后结果是得到了一条链,答案也就变成了这条链上的点权之和。
实现也不难,把每个父节点向子节点连一条单向边,dfs 到最深然后逐层合并,每条链的头没有对应的点,合并后再加入,链用优先队列维护很方便。
Code:
const int N=4e6+5;
int n;
ll ans;
int a[N],zc[N],id[N],tot;
int hh[N],to[N],ne[N],cnt;
priority_queue<int>q[N];
namespace Wisadel
{
void Wadd(int u,int v)
{
to[++cnt]=v;
ne[cnt]=hh[u];
hh[u]=cnt;
}
void Wmerge(int x,int y)
{
if(q[id[x]].size()<q[id[y]].size()) swap(id[x],id[y]);
int len=q[id[y]].size();
fo(i,1,len)
zc[i]=max(q[id[x]].top(),q[id[y]].top()),
q[id[x]].pop(),q[id[y]].pop();
fo(i,1,len) q[id[x]].push(zc[i]);
}
void Wdfs(int u)
{
id[u]=++tot;
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
Wdfs(v);
Wmerge(u,v);
}
q[id[u]].push(a[u]);
}
short main()
{
memset(hh,-1,sizeof hh);
n=qr;int f;
fo(i,1,n) a[i]=qr;
fo(i,2,n) f=qr,Wadd(f,i);
Wdfs(1);
while(q[id[1]].size()) ans+=q[id[1]].top(),q[id[1]].pop();
printf("%lld\n",ans);
return Ratio;
}
}
int main(){return Wisadel::main();}
完结撒花~
久违了