序言
去年因为疫情没有举办省赛,导致两年的省赛隔了一年,少参加了一次,血亏。
四月ACM内部选拔赛的时候,用了2017年的山东省省赛题,用个人赛的形式打的,做出来五题,放在当年是可以拿银牌的。队友wzk也是五题,一样是银,感觉我们队应该能拿金Au。
赛前
热身赛过了样例就交,提前下班,第六个ak的(虽然最后掉到了31名),喜忧参半。
正式赛的上午11点,但是我们9点左右就坐下了,等着考试……
我也不知道我是怎么度过的这一个多小时,毕竟是自己在ACM打的最后一场比赛了,务必拿金退役了才能无憾……
wzk根据气球颜色分析题目难度,认为L题和J题对应的颜色比较显眼,我们的策略就是先开L题和J题。
比赛时
纸质版题目迅速翻阅,题目没有很短的,
aljt一直看题,我想看但是看不懂,wzk也看不下去,只能一直刷榜……
先看的L和J题直接看傻了,只能看榜锁定题目……
3分钟过去了,还没首A…… 。
我看了眼I的样例,感觉像是个普通的模拟题,让aljt赶紧看看I,
说不定是题面很长的签到题呢,aljt看了一眼直接说不可做……
五分钟,G题有首A了,火速看G,火速翻译。
给定
n
n
n 个数,求其平均数,保留小数点后
k
k
k 位小数……,n、k都是1e5
乍一看脑子就短路了,三个人都没很快想到怎么做。
这时候突然又有人过了
M
M
M 题,aljt和wzk两人迅速看M,留下我一个人继续想这道题。
大概过了五分钟吧,我突然想到模拟除法运算就可以了,他俩还没读完题,我把做法一说,wzk火速敲代码,过了。
#include<bits/stdc++.h>
using namespace std;
int n, k, tot;
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
tot += x;
}
printf("%d.",tot / n);
tot %= n;
for(int i = 1; i <= k; i++)
{
tot = tot * 10;
printf("%d", tot / n);
tot %= n;
}
return 0;
}
排名五六十的样子,还好,还保在银牌线上。
M题,给定一个矩阵C,让你构造两个和C相同大小的矩阵A和B,当同一个位置都为1时C的对应位置也是1,并要求A和B的1是个连通块。听完题意我们仨又卡住了…
这时候又有队伍过了H题,我们又火速去看H题,H题像一个背包题, n n n 个物品,每个物品消耗 h i h_i hi 点血和 s i s_i si 点体力,血不能全耗净,体力值耗净后消耗血……集体再次沉默……看数据范围 N = 1000 N=1000 N=1000 , H = S = 300 H = S = 300 H=S=300。状态有 1000 ∗ 300 ∗ 300 1000 * 300 * 300 1000∗300∗300种,好像去年数学建模的B题沙漠旅行一样。所有状态一共 9 e 7 9e7 9e7种,全枚举一遍就可以,但是空间不够用啊……我又想到了一个类似开大空间的dp题目Glass Half Spilled
这时候wzk认为这两题可以继续放着,然后发现有一个队伍过了C,wzk当机立断直接让aljt继续开C,我心态很乱,看不下去第三道题了,继续想M和H题。思考的时候wzk跟安霖久泰在讨论二进制拆分问题,好像C题是染色,把树变成多叉树,然后下面分类染……然后跟我快速讲了一下题意和做法,我感觉也像一个简单的构造题,认为可以做,就让wzk去写了,我继续想M和H题。
脑子同时想M和H题,思路很乱。看数据范围突然想到二维背包问题,
枚举每个物品,再枚举消耗的血量,再枚举体力值,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示血量为
i
i
i 体力为
j
j
j 时候的最大收益,它的状态是由
i
+
h
i
i + h_i
i+hi的血量 和
j
+
s
i
j + s_i
j+si的体力值转移过来的,就这样过了,说给wzk了,wzk秒懂…… 表示写完C题 调不出来就去写这个dp。
C题写了大概十多分钟,测了几组,过了
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
long long k;
vector<pair<int,int> > ans;
int main(){
ans.clear();
scanf("%lld",&k);
k--;
if(k==1){
puts("1");
return 0;
}
int lst=1,tot=1;
while(k>0){
int cnt=0;
while(k%2==0 && k>0){
k/=2;
cnt++;
}
for(int i=1;i<=cnt;i++){
tot++;
ans.push_back({lst,tot});
}
k--;
if(k>0){
tot++;
ans.push_back({lst,tot});
lst=tot;
}
}
printf("%d\n",tot);
for(auto i:ans){
tot--;
printf("%d %d",i.fi,i.se);
if(tot!=1) puts("");
}
return 0;
}
赶紧去补dp题,我又跟wzk重述了一下状态转移,分用血换体力和不换体力两种状态,代码调了一会,但是思路一直是正确的,也没有花太多时间就过了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[330][330],h[1010],s[1010],w[1010];
signed main(){
int n,H,S;
scanf("%lld%lld%lld",&n,&H,&S);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&h[i],&s[i],&w[i]);
}
memset(f,0,sizeof(f));
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=H;j++){
if(j+h[i]>H) break;
//k=0
for(int k=0;k<=s[i];k++){
if(j+h[i]+s[i]-k>H) continue;
if(k>S) continue;
f[j][0]=max(f[j][0],f[j+h[i]+s[i]-k][k]+w[i]);
}
for(int k=1;k<=S;k++){
if(k+s[i]>S) continue;
f[j][k]=max(f[j][k],f[j+h[i]][k+s[i]]+w[i]);
}
}
}
for(int i=1;i<=H;i++){
for(int j=0;j<=S;j++){
ans=max(ans,f[i][j]);
// printf("f[%lld][%lld]=%lld\n",i,j,f[i][j]);
}
}
printf("%lld",ans);
return 0;
}
排名赶到30+,距离金牌线就差一点了。
aljt翻译给我了B题,简单模拟一下就好。现在状态才慢慢回来了,我继续想M题,然后B题过的人太少了,还是用随机数生成的数据,这种题型我没练过,精力还在M上……
D题翻译给wzk的时候 ,wzk直接秒了,用map保存的状态,样例和手造样例直接过了,交了一发T了……考虑用数组去记录状态,消除map带来的巨大常数,然后又交了一发过了,nb!!!。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a[200110],b[200110],ansa,ansb;
signed main(){
scanf("%lld",&n);
ansa=0;
ansb=0;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++){
int x,y;
scanf("%lld%lld",&x,&y);
x+=10,y+=10;
ansa+=4;
ansb+=4;
a[x]++;
b[y]++;
if(a[x]!=1) ansa-=2;
if(a[x-1]>=a[x]) ansa-=2;
if(a[x+1]>=a[x]) ansa-=2;
if(b[y]!=1) ansb-=2;
if(b[y-1]>=b[y]) ansb-=2;
if(b[y+1]>=b[y]) ansb-=2;
printf("%lld %lld",ansa,ansb);
if(i!=n) puts("");
}
return 0;
}
4道题从60+名慢慢杀回25名,超越了myj他们队,这时候M题还是没什么进展,我让aljt重新读读题。
结果发现发现原来题目数据保证最外围一定都是0。可我还是不会做,我造了个数据
0000000
0111110
0100010
0101010
0100010
0111110
0000000
然后被迫放弃了,我实在做不来构造题。
这时候aljt推荐我去做做B题,我就去看B了。
B的题目数据构造是随机生成的……我们尝试打表看看他的构造函数是不是有规律可循,但是实在是没看出来……wzk问我以前做没做过这种数据构造题,我说没有……毕竟当年准备noip的时候这种题型对应的难度是我没必要准备的……就有点感觉绝望了……
我突然想到一个性质,如果随机数生成了一个质数,那么这个题基本可以确定是输出n-1了(虽然可以被卡掉,但方向是对的)。
突然wzk说会做M了,两个构造矩阵分别染左上和右下,然后分奇偶去染色。
说这题是去年上海站的某道构造题,也是分奇偶的,我听了听感觉挺对。
然后wzk去学M了,我们手玩了几个样例,他去写了。十几分钟后就写完了,测一下我和aljt造的样例。过了!!!
提交! 又一遍过了!
#include<bits/stdc++.h>
using namespace std;
int a[600][600];
int b[600][600],c[600][600];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%1d",&a[i][j]);
b[i][j]=c[i][j]=a[i][j];
}
}
for(int i=1;i<=n;i++){
b[i][1]=1;
}
for(int i=1;i<=n;i++){
c[i][m]=1;
}
for(int i=1;i<=n;i+=2){
for(int j=2;j<m;j++){
b[i][j]=1;
}
}
for(int i=2;i<=n;i+=2){
for(int j=2;j<m;j++){
c[i][j]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%d",b[i][j]);
}
puts("");
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%d",c[i][j]);
}
if(i!=n) puts("");
}
return 0;
}
现在六次 提交,五次通过,罚时几乎没有!但是发现排名还是25……
但我感觉B题可以出!!
我们三个人回来想B,我发现模数(R-L+1) 为1时,答案是(n-1)*L.
如果模数为2,那么图中很多点对的边权都为1,答案是n-1。
感觉模数只要不为1,随机生成的点权很容易也为1,答案也总是n-1.
我觉得n比较小的时候跑暴力,大的时候直接输出n-1,模数为1的时候特判!。
我直接敲代码,过了!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,L,R,a[200001];
unsigned long long seed;
unsigned long long xorshift64(){
unsigned long long x=seed;
x^=x<<13;
x^=x>>7;
x^=x<<17;
return seed=x;
}
int gen(){
return xorshift64()%(R-L+1)+L;
}
struct pro
{
int u, v, w;
}s[1000 * 1000 + 10];
int fa[5050];
int gcd(int a, int b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
bool cmp(pro a, pro b)
{
return a.w < b.w;
}
int find(int x)
{
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
signed main(){
scanf("%lld%lld%lld%llu",&n,&L,&R,&seed);
for(int i=1;i<=n;i++){
a[i]=gen();
// cout<<i<<":"<<a[i]<<endl;
}
if(n <= 1000)
{
long long Ans = 0;
for(int i = 1; i <= n; i++) fa[i] = i;
int tot = 0;
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++)
s[++tot].u = i, s[tot].v = j, s[tot].w = gcd(a[i], a[j]);
sort(s+1, s+1+tot, cmp);
for(int i = 1; i <= tot; i++)
{
int u = s[i].u, v = s[i].v;
int X = find(u), Y = find(v);
if(X != Y)
{
fa[X] = Y;
Ans += s[i].w;
}
}
cout << Ans << endl;
return 0;
}
if(L == R)
{
long long Ans = (n - 1) * L;
cout << Ans << endl;
return 0;
}
cout << n - 1 << endl;
return 0;
}
排名直接杀回10+。。看了看其他题目,剩余所有题只有7发通过,感觉可以下班了……
剩余的一个多小时里,我们看了A,学了下围棋,看之前是0-0,看完之后是1-3,感觉可以做!讨论了很久没有结果……我一直在想封榜的时候不会被后面的队伍反超吧。还好没有。
后来aljt和wzk去写J题了,我没什么好思路,就等着下班了……
心态实在是很累,前三题虽然没有罚时,但是A的实在是太晚了……
封榜的一个小时心乱如麻,wzk写完J题过了样例,直接提交,但是wa了。最后二十分钟一直在调,但还是wa……
比赛结束,倒计时半分钟公布排行榜……
去掉打星队是第十七名。Au了!!
myj队伍是第十名,也是Au!!
青岛科技大学实现金牌零的突破了,直接二金。
xjk队伍最后半小时连过三题,可惜罚时太大,拿到了银牌前列的位置……
2金1银5铜……
结束
写于2021.05.12,退役已经三天,不知未来去向…