5题金 4题银铜 3题快铜
ACM生涯第一场ICPC就差点银,开心~
比赛后还得知DRX赢了,冠军皮肤选的千珏,更开心了~
D.DRX vs T1
签到题
题意:
输入5个字符,D代表DRX赢,T代表T1赢,?代表不知道谁赢。输出可能赢的队伍。
解法:
统计D和T的数量,若D>=T,那么DRX赢,否则T1赢。因为可以把?全部看作数量多的队伍赢的。
本人千珏绝活,DRX韩一珏Pyosik粉丝,很希望DRX能赢,这样韩一珏就能选千珏冠军皮肤了。所以写代码的时候特意整活,让判定条件是D>=T,这样D==T的时候,两边都可能赢都是正确答案,输出DRX。(本来想写d+?>t就输出DRX,但是担心出锅,没敢这么写) 打完比赛拿了牌子还得知DRX真夺冠了,超超超开心!!!
核心代码:
char x;
ll d, t;
void Solve(void)
{
d = t = 0;
FOR(i, 1, 5)
{
cin >> x;
if (x == 'D')
++d;
if (x == 'T')
++t;
}
if (d >= t)
cout << "DRX" << edl;
else
cout << "T1" << edl;
return;
}
C.Clamped Sequence
简单题
题意:
有n个数,一个限制长度d。选择一段范围[l,r],要求r-l<=d,使得这n个数中,比l小的变为l,比r大的变为r,在范围内的则不变,问n-1个差分的绝对值的和的最大值。
解法:
首先想到,选择一个范围一定只会让原本数组更劣,不会更优,选择范围是劣化操作。那么希望这个操作能影响的数最少,即让这个范围大小最大,为d。
之后想到,既然希望影响的数最少,那么一定是更多的数在区间内,就是说,l或者r,一定有一个是卡在数组里的一个数上。
n只有1e3的数量级,说明可以n^2解,那么直接枚举每一个数作为上限/下限,寻找最优答案。
因为复杂度够,所以比赛的时候写的很暴力,没有任何优化。
赛中想到一端要卡住后没仔细想可能另一端卡住更优也要考虑,只一个Check(a[i],a[i]+d)WA了一发,难受,其实复杂度够保险点在多加几组进去也不要紧。
写完AC那发后因为前一发WA一直不敢交,还好队友*1够果断直接催着交过了。
核心代码:
ll n, d;
ll a[MXN];
ll b[MXN];
ll ans;
void Check(ll l, ll r)
{
FOR(i, 1, n)
if (a[i] < l)
b[i] = l;
else if (a[i] > r)
b[i] = r;
else
b[i] = a[i];
ll res = 0;
FOR(i, 1, n - 1)
{
ll tmp = b[i] - b[i + 1];
if (tmp < 0)
tmp = -tmp;
res += tmp;
}
ans = max(ans, res);
return;
}
void Solve(void)
{
ans = -LNF;
cin >> n >> d;
FOR(i, 1, n)
cin >> a[i];
FOR(i, 1, n)
{
Check(a[i], a[i] + d);
Check(a[i] - d, a[i]);
}
cout << ans << edl;
return;
}
L.Tavern Chess
模拟题
题意:
A有n个角色,B有m个角色。角色多的先手,角色数量相同,先手概率一半一半。两边的角色轮流攻击对方,攻击者是当前队伍中攻击过次数最少的,攻击次数相同时取最左的;被攻击者是对面还活着的随机一个,每个活着的被选中概率相等。攻击时,对手会扣自己的攻击力,同时自己会扣对手的攻击力,所以可能平局。扣血后血量会掉,但是攻击力不会掉。血量小于等于0就判定为死亡。问A赢的概率,B赢的概率,平局的概率。
解法:
用DFS暴力搜索所有情况,即可得到答案。
赛中因为一直读错题,先是当成最左边的攻击,再是当成攻击力最低的攻击,导致一直过不了样例,以为是代码写太长出BUG了。上百行的代码全删重写来了五六遍,写麻了,才发现是题读错了。(实际上每一遍按照理解错的题目都是对的)
代码很长,因为懒得用函数统一写,这样写不用脑子写的快。
核心代码:
db aw, bw, dw;
ll n, m;
ll a[MXN];
ll b[MXN];
struct hp
{
ll aCnt[MXN];
ll bCnt[MXN];
ll aHP[MXN];
ll bHP[MXN];
};
bool Check(hp now, db pp)
{
ll liveA = 0;
ll liveB = 0;
FOR(i, 1, n)
if (now.aHP[i] > 0)
++liveA;
FOR(i, 1, m)
if (now.bHP[i] > 0)
++liveB;
if (liveA == 0 && liveB == 0)
{
dw += pp;
return true;
}
if (liveA == 0)
{
bw += pp;
return true;
}
if (liveB == 0)
{
aw += pp;
return true;
}
return false;
}
void DFS(int team, hp now, db p)
{
if (team == 0)
{
ll ATKer;
ll mn = LNF;
FOR(i, 1, n)
if (now.aHP[i] > 0)
if (now.aCnt[i] < mn)
{
ATKer = i;
mn = now.aCnt[i];
}
++now.aCnt[ATKer];
ll live = 0;
FOR(i, 1, m)
if (now.bHP[i] > 0)
++live;
db p2 = 1.0;
p2 /= live;
FOR(i, 1, m)
{
if (now.bHP[i] <= 0)
continue;
hp nxt = now;
nxt.aHP[ATKer] -= b[i];
nxt.bHP[i] -= a[ATKer];
if (Check(nxt, p * p2) == false)
DFS(1, nxt, p * p2);
}
return;
}
if (team == 1)
{
ll ATKer;
ll mn = LNF;
FOR(i, 1, m)
if (now.bHP[i] > 0)
if (now.bCnt[i] < mn)
{
ATKer = i;
mn = now.bCnt[i];
}
++now.bCnt[ATKer];
ll live = 0;
FOR(i, 1, n)
if (now.aHP[i] > 0)
++live;
db p2 = 1.0;
p2 /= live;
FOR(i, 1, n)
{
if (now.aHP[i] <= 0)
continue;
hp nxt = now;
nxt.bHP[ATKer] -= a[i];
nxt.aHP[i] -= b[ATKer];
if (Check(nxt, p * p2) == false)
DFS(0, nxt, p * p2);
}
return;
}
return;
}
void Solve(void)
{
aw = bw = dw = 0.0;
cin >> n >> m;
FOR(i, 1, n)
cin >> a[i];
FOR(i, 1, m)
cin >> b[i];
hp tmp;
FOR(i, 1, n)
{
tmp.aHP[i] = a[i];
tmp.aCnt[i] = 0;
}
FOR(i, 1, m)
{
tmp.bHP[i] = b[i];
tmp.bCnt[i] = 0;
}
if (n == m)
{
DFS(0, tmp, 0.5);
DFS(1, tmp, 0.5);
}
else if (n > m)
DFS(0, tmp, 1.0);
else if (m > n)
DFS(1, tmp, 1.0);
cout << SPO(15) << aw << edl;
cout << SPO(15) << bw << edl;
cout << SPO(15) << dw << edl;
return;
}
F.Half Mixed
构造题
题意:
给出n和m,要求构造一个n×m的01矩阵。定义一个只有0或者只有1的矩阵为纯净矩阵,有0且有1的为混乱矩阵。要求构造的矩阵的所有子矩阵中,纯净矩阵数量和混乱矩阵数量相同。
解法:
看到数据范围的时候非常亲切,因为前段时间给新生出新生赛的时候就用这种数据恶心过新生。n和m最大都可能是1e6,如果想用数组提前存了再输出就要1e6×1e6共1e12,显然空间是不够的,所以不能用二维数组来存。
容易想到n*m的矩阵,一共有((n*n+n)/2)* ((m*m+m)/2)个子矩阵。所以这个数一定得是偶数,否则构造不出,直接输出No。
再然后,想到一列0一列1这样交替,那就有((n*n+n)/2)*m个子矩阵是纯净矩阵。
距离总数1/2还需要修正。于是想到再加列,连续两列1,其他交替,那就多了((n*n+n)/2)个子矩阵。连续三列1,就多了((n*n+n)/2)*(1+2)个。最后统一为,连续k列,可以多((n*n+n)/2)*((k*k+k)/2)个。行同理。
于是,这道题就转变为了,用1,3,6,10...这种数去构造((m*m+m)/4)-m(或者构造((n*n+n)/4)-n)。
因为行列很容易弄混,之前第三题L题又一直在反复写代码,写这题的时候已经有点神志不清了。还好队友三人分开想,最后都想到了同一个想法,然后就是我敲代码,队友帮忙处理细节。
核心代码:
ll n, m;
ll a[MXN];
vector<ll> ve;
void Init(void)
{
a[1] = 1;
FOR(i, 2, MXN - 3)
a[i] = a[i - 1] + i;
return;
}
void Solve(void)
{
ve.clear();
cin >> n >> m;
ll row = (n * n + n) / 2;
ll col = (m * m + m) / 2;
ll tot = row * col;
if (tot & 1)
{
cout << "No" << edl;
return;
}
ll f = 0;
if (col % 2 == 0)
{
ll aim = col / 2 - m;
ll sum = 0;
ROF(i, m + 1, 1)
{
if (aim == 0)
break;
ll k = aim / a[i];
FOR(j, 1, k)
{
ve.push_back(i + 1);
sum += i + 1;
}
aim %= a[i];
}
f = 1;
FOR(i, 1, m - sum)
ve.push_back(1);
}
else if (row % 2 == 0)
{
ll aim = row / 2 - n;
ll sum = 0;
ROF(i, n + 1, 1)
{
if (aim == 0)
break;
ll k = aim / a[i];
FOR(j, 1, k)
{
ve.push_back(i + 1);
sum += (i + 1);
}
aim %= a[i];
}
f = 2;
FOR(i, 1, n - sum)
ve.push_back(1);
}
if (f == 0)
{
cout << "No" << edl;
return;
}
cout << "Yes" << edl;
if (f == 1)
{
FOR(i, 1, n)
{
bool flag = true;
ll ch = 1;
for (auto it : ve)
{
FOR(j, 1, it)
{
if (flag)
flag = false;
else
cout << ' ';
cout << ch;
}
ch = 1 - ch;
}
cout << edl;
}
}
if (f == 2)
{
ll ch = 1;
for (auto it : ve)
{
FOR(i, 1, it)
{
bool flag = true;
FOR(j, 1, m)
{
if (flag)
flag = false;
else
cout << ' ';
cout << ch;
}
cout << edl;
}
ch = 1 - ch;
}
}
}
int main(void)
{
std::ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
// #ifdef cincoutcmd
// freopen("cin.txt", "r", stdin);
// freopen("cout.txt", "w", stdout);
// #endif
Init();
int t;
cin >> t;
while (t--)
Solve();
return 0;
}
这场L题读错题太伤了,浪费了几个小时的罚时,而且很搞心态。导致后面F时间也不多,慌忙交题白wa了好几发(甚至因为赶时间最开始输出格式都是错的)。不读错题应该能直接ACM生涯首银,有点可惜。下个目标---首银!