【B类竞赛】第十一届山东省大学生程序设计竞赛 比赛经历

序言

去年因为疫情没有举办省赛,导致两年的省赛隔了一年,少参加了一次,血亏。
四月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 1000300300种,好像去年数学建模的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,退役已经三天,不知未来去向…

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值