开幕雷击系列。
最后一题真的神仙,不会。
C题:
发觉这东西好像可以背包。
然后背出来枚举一枚举就行。
貌似直接枚举也行?
注意判答案是时候有个式子
S
u
g
a
r
1
T
o
t
1
<
S
u
g
a
r
2
T
o
t
2
\frac{Sugar_1}{Tot_1}<\frac{Sugar_2}{Tot_2}
Tot1Sugar1<Tot2Sugar2满足这条件的时候应该替换答案。
如果初始值并没有设置成
T
o
t
=
a
,
S
u
g
a
r
=
0
Tot=a,Sugar =0
Tot=a,Sugar=0,那么上面那个式子需要加等号。不然万一给出巧妙的数据,让你最后得到一杯无糖水,不加等号就会挂。
本来准备实况的,然后开幕雷击之后就光速掐掉了HHHHHHHH
#include <bits/stdc++.h>
using namespace std;
int n, m;
bool wat[6010];
bool sug[6010];
int A, B, C, D, E, F;
int main() {
scanf("%d%d%d%d%d%d", &A, &B, &C, &D, &E, &F);
wat[0] = 1;
for(int i=0;i<=3000;++i)
wat[i+A] |= wat[i];
for(int i=0;i<=3000;++i)
wat[i+B] |= wat[i];
sug[0] = 1;
for(int i=0;i<=3000;++i)
sug[i+C] |= sug[i];
for(int i=0;i<=3000;++i)
sug[i+D] |= sug[i];
long long watsug = 100, nsug = 0;
for(int i=1;i*100<=F;++i) {
if(wat[i]) {
for(int j=0;j<=3000;++j) {
if(!sug[j])
continue;
if(E * i >= j) {
if(i * 100 + j > F)
continue;
long long nwtot = i * 100 + j;
long long nwsug = j;
if(nwsug * watsug >= nwtot * nsug) //就是这里的等号
watsug = nwtot, nsug = nwsug;
}
}
}
}
printf("%lld %lld\n", watsug, nsug);
return 0;
}
//没加那个等号的话,5 6 7 8 100 500就能卡。
D题:
这题有点意思,适合拿来签到。
一个小性质:最短路一定是某两条最短路拼起来。
所以
O
(
n
3
)
O(n^3)
O(n3)枚举边和中间点,如果他能被拼起来恰好等于,那么这条新的边废了。
如果他比拼起来的还要长,那么这图挂了。
#include <bits/stdc++.h>
using namespace std;
int n;
int mp[510][510];
int res[510][510];
int main() {
scanf("%d", &n);
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
scanf("%d", &mp[i][j]);
}
}
bool valid = 1;
for(int l=1;l<=n;++l) {
for(int r=1;r<=n;++r) {
bool ok = 0;
for(int mid=1;mid<=n;++mid) {
if(mid == l || mid == r)
continue;
if(mp[l][r] == mp[l][mid] + mp[mid][r]) {
ok = 1;
}
if(mp[l][r] > mp[l][mid] + mp[mid][r]) {
valid = 0;
}
}
if(!ok) {
res[l][r] = mp[l][r];
}
}
}
long long ans = 0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
ans += res[i][j];
if(!valid)
ans = -2;
cout<<ans/2;
return 0;
}
//5 6 1 1 100 500
E题:
分析一下发觉,我们在保证同色的前提下,让异色尽量小就完事了。
证明在注释里(
然后发觉颜色并不重要,因为我们可以随时inverse一个子树。
然后发现他就是个长得很像背包的东西了,树上DP一下,转一转。
感觉这题可以魔改一下www
注释还是赛时思考的时候写的
#include <bits/stdc++.h>
using namespace std;
vector<int>ed[1010];
int dp[2010];
int pac[5010][6010];
int lim[3010];
const int ma = 0x3f3f3f3f;
bool possible = 1;
int pcnt, n;
inline void ade(int l, int r) {
ed[l].push_back(r);
ed[r].push_back(l);
}
void solve(int nw, int fro) {
if(!possible)
return;
for(int i=0;i<ed[nw].size();++i) {
if(!possible)
return;
int tar = ed[nw][i];
if(tar == fro)
continue;
solve(tar, nw);
}
if(!possible)
return;
++pcnt;
pac[pcnt][0] = 0;
for(int i=0;i<ed[nw].size();++i) {
int tar = ed[nw][i];
if(tar == fro)
continue;
int val1 = dp[tar], val2 = lim[tar];
for(int j = 0; val1 + j <= 5000; ++j)
pac[pcnt + 1][val1 + j] = min(pac[pcnt + 1][val1 + j], pac[pcnt][j] + val2);
for(int j = 0; val2 + j <= 5000; ++j)
pac[pcnt + 1][val2 + j] = min(pac[pcnt + 1][val2 + j], pac[pcnt][j] + val1);
++pcnt;
}
int ano = ma;
for(int i=0;i<=lim[nw];++i)
ano = min(ano, pac[pcnt][i]);
if(ano == ma) {
possible = 0;
return;
}
else
dp[nw] = ano;
return;
}
int main() {
memset(pac, 0x3f, sizeof(pac));
scanf("%d", &n);
int fa;
for(int i=2;i<=n;++i)
scanf("%d", &fa), ade(i, fa);
for(int i=1;i<=n;++i)
scanf("%d", &lim[i]);
solve(1, 1);
printf(possible ? "POSSIBLE" : "IMPOSSIBLE");
return 0;
}
/*
* 重量轻的情况一定优于重量重的情况
* 吗?
* mdzz这不废话,如果他瘦了,你在根让他胖回来就完事。
* 然后那个分配重量就变成了最小化一个重量。
* emm好像并不太需要关心这个子树里面究竟是什么颜色。
* 假设他的根为某种颜色,我们直接对该子树进行颜色reverse,一样是不发生变化的
* 那么,DP[i]表示,当前做了i号点的限制,i子树里面,另一个颜色的最小sum是DP[j]
* 怎么感觉是个背包变形啊。
* 对,貌似还真是个背包变形。
* 考虑转移,在i的时候,开个pac[i][5010],初始让pac[i][0] = 0, 其他都是inf
* 枚举所有的儿子,每次俩转移:转或不转。
* 注意不能继承上一次的状态,一定得新转,继承会挂。
* 好麻烦.jpg
* emm我还得滚一下咯?
* 诶不对。我开个cnt就完事好像。
*
*/
F:
神仙,不会,GG,跑路
能想到建图,然后顺序那块完全处理不动。
先来说说我想到的吧。
把稽器人机器人当成点,球当成边。
则,一个球只可能被两个机器人中的一个拿走,一个边两个点,非常科学。
建个图,变成给每个点找个边与它对应,要求不重不漏,图保证点数=边数。
显然只有基环树/基环树森林有解。
然后就不会了qwq。顺序那里想不动。
跑路之后膜了波题解爸爸,还是没看太懂。
被拓扑序那块搞懵逼了。
就先坑在这里吧。
Orz题解爸爸
https://www.luogu.com.cn/problem/solution/AT3537
另外吐槽一句luogu的题目难度评级
这题和E题居然都是紫题
怎么感觉他俩显然不是一个难度等级的
果然大家都是在用脚投票吗
另外就是,在找题解的时候,我看到了这么一句话。
BKSN Atcoder!