CCPC-Wannafly Camp Day6 总结

CCPC-Wannafly Camp Day6 总结

yysy,感觉最后几题是临时加上的签到题(们)。
所以,这篇总结先放最后四题。

7-11 6K. 最大权值排列

对于一个长度为 n 的数列 A,定义它的权值 f(A) 为每一个区间平均数的和,即

f(A)=
​i=1
​∑
​n
​​
​j=i
​∑
​n
​​
​j−i+1

​∑
​k=i
​j
​​ A
​k
​​
​​
现在给出了整数 n,你需要给出一个 1…n 的排列 P 使得 f§ 尽可能大。如果有多个不同的排列权值相同,则给出字典序最小的那个。

一个排列 a[1…n] 的字典序比 b[1…n] 的字典序小,当且仅当存在某个 j 满足 a[1…j−1]=b[1…j−1] 且 a[j]<b[j]

输入格式
一行一个正整数 n (1≤n≤10​e5).

输出格式
一行n个数,表示答案的排列,行末需要由空格

输入样例
5

输出样例
1 3 5 4 2

思路

盲猜像题目里那样排列。
最大数肯定要出现最多次,所以还是有道理的。

#include<iostream>;
#include<algorithm>;
#include<cstring>;
using namespace std;
int a[100010];
int main(){
	int i,j,k,n,m;
	cin>>n;
	if(n%2==0){
		for(i=1;i<=n/2;i++){
			a[i]=i*2-1;
			a[n-i+1]=i*2;
		}
		for(i=1;i<=n;i++) cout<<a[i]<<" "; 
	}
	else{
		for(i=1;i<=n/2;i++){
			a[i]=i*2-1;
			a[n-i+1]=i*2;
		}
		a[n/2+1]=n;
		for(i=1;i<=n;i++) cout<<a[i]<<" "; 
	}
	return 0;
}

7-12 6L. 你吓到我的马了.jpg

给定一个 n×m 的棋盘,棋盘上有很多障碍和一个中国象棋中的马(本题中马的移动方式跟中国象棋中的马完全一致)。

马每次移动首先会选择是上下左右中的某个不被卡马脚(卡马脚的定义在下面)的方向并面朝这个方向,然后再选择跳到左前方或者右前方,其中左前方是前面两格,左边一格的地方,右前方是前面两格,右边一格的地方。

然后马的移动有以下这么几个条件限制:

  1. 马不能跳到障碍上或者跳出棋盘

  2. 当马在某个方向上的前面一个格子有障碍的话,那么我们称马在这个方向上被卡了马脚

现在对于棋盘上的每个点,你需要输出马最少跳几次能跳到这个地方,如果不可能跳到这个地方则输出 -1.

输入格式
第一行两个正整数 n,m 表示棋盘大小

接下来 n 行,每行一个长度为 m 的字符串,表示地图

第 i 行第 j 列的格子由第 i 行的字符串的第 j 个(从 1 开始计算)字符决定,若是 . 则表示是空格子, X 表示障碍,M 表示马,数据保证马有且只有一个。

2≤n,m≤100

输出格式
输出 n 行,每行 m 个数,第 i 行第 j 个数表示跳到第 i 行第 j 列至少需要几步,如果不可能跳到则输出 −1。

输入样例
3 3
.M.

.X.

输出样例
-1 0 -1
-1 -1 -1
1 -1 1

思路

搜索就完了。

#include<iostream>
#include<algorithm>
using namespace std;
int mx, my, n, m;
char map[110][110];
bool book[110][110];
int qfront=0, qtail=1;
int zou[8][4]={-1,2,0,1, 1,2,0,1, 2,1,1,0, 2,-1,1,0, 1,-2,0,-1, -1,-2,0,-1, -2,-1,-1,0, -2,1,-1,0};
struct node{
	int x, y, step=-1;
}q[100010];
void bfs()
{
	
	q[0].x=mx;
	q[0].y=my;
	q[0].step=0;
	book[mx][my]=1;
	while(qfront<=qtail)
	{
		for(int i=0; i<8; i++)
		{
			int tx=q[qfront].x, ty=q[qfront].y, ts=q[qfront].step;
			if(map[tx+zou[i][0]][ty+zou[i][1]]=='.'&&map[tx+zou[i][2]][ty+zou[i][3]]!='X'&&book[tx+zou[i][0]][ty+zou[i][1]]==0)
			{
				book[tx+zou[i][0]][ty+zou[i][1]]=1;
				q[qtail].x=tx+zou[i][0];
				q[qtail].y=ty+zou[i][1];
				q[qtail].step=ts+1;
				qtail++;
				//cout<<tx+zou[i][0]<<" "<<ty+zou[i][1]<<" "<<i<<endl;
			}
		}
		qfront++;
	}
} 
bool cmp(node a, node b)
{
	if(a.x!=b.x)
	{
		return a.x<b.x;
	}
	else 
		return a.y<b.y;
}
int main()
{
    cin>>n>>m;
    for(int i=0; i<110; i++)
    {
    	for(int j=0; j<110; j++)
    	{
    		map[i][j]='X';
    		book[i][j]=0;
		}
	}
    for(int i=1; i<=n; i++)
    {
    	for(int j=1; j<=m; j++)
    	{
    		cin>>map[i][j];
    		if(map[i][j]=='M')
    		{
    			mx=i;
    			my=j;
			}
		}
	}
	bfs();
	sort(q, q+qtail, cmp);
	int k=0;
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<m; j++)
		{
			if(q[k].x==i&&q[k].y==j)
				cout<<q[k++].step<<" ";
			else 
				cout<<-1<<" ";
		}
		if(q[k].x==i&&q[k].y==m)
			cout<<q[k++].step;
		else 
			cout<<-1;
		cout<<endl;
	}
} 

7-13 6M. 自闭

对于一场比赛,一共有 n 个参赛选手和 m 个题,为了方便我们假设一个提交记录的结果只有 WA 和 AC 两种,我们用以下规则定义一个选手的自闭程度:(以下规则优先级从高到低,如有冲突(例如规则 3,6),以编号小的规则为准)。

  1. 如果一个选手没有做出任何提交,则最后他的自闭程度为 998244353

  2. 如果一个选手没有 AC 任何一道题,则最后他的自闭程度为 1000000

  3. 如果一个选手 AC 了所有题,则最后它的自闭程度为 0

  4. 对于一个题,如果该题最后有人通过但这个选手没通过,则这个选手的自闭程度增加 20

  5. 对于一个题,如果至少 ⌊n/2⌋ 个选手 AC 了这个题,但某个选手没通过,则该选手的自闭程度在规则 4 的基础上再额外增加 10

  6. 在某个题中,如果该选手连续 WA 了最多 K 次(也是就该题的某 K 次连续提交都是 WA),则该选手自闭程度增加 K
    ​2
    ​​

  7. 在某个题中,如果该选手连续 WA 了最多 K 次且最后没有 AC 这题,则该选手自闭程度再额外增加 K
    ​2
    ​​

现在按时间顺序给出了一场比赛的提交记录,你需要求每个选手的自闭程度。

一些细节:在某道题上,AC 了之后再进行的提交也会被计算入规则 6 中。

输入格式
第一行三个整数 n,m,W,表示选手数量,题目数量和提交记录数量

接下来 W 行,每行三个整数 x,y,c,描述一条提交记录:选手 x 交了题目 y,c=0 表示 WA 了,c=1 表示 AC 了。

1≤n≤100,1≤m≤10,1≤W≤5000

输出格式
输出 n 行,第 i 行一个整数表示第 i 个选手的自闭程度。

输入样例
4 3 13
1 1 1
1 2 1
1 3 1
2 1 0
2 1 0
2 1 1
2 2 0
3 1 0
3 1 0
3 1 0
3 2 1
3 2 0
3 2 0

输出样例
0
56
72
998244353

思路

模拟,一定要细心。我队因为一个小错误改了好久。MD

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
struct all
{
	bool ac[15];
	int zibi;
	int cishu[15];
	int zuida[15];
};
all a[105];
bool cnm[105][15];
int zongti[15];
int main()
{
	int n,m,w;
	cin>>n>>m>>w;
	for(int i=1; i<=m; i++) zongti[i]=0;
	for(int i=1; i<=n; i++)
	{
		
		a[i].zibi=0;
		for(int j=1; j<=m; j++)
		{
			cnm[i][j]=0;
			a[i].ac[j]=0;
			a[i].cishu[j]=0;
			a[i].zuida[j]=0;
		}
	}
	for(int i=1; i<=w; i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		if(z==0)
		{
			a[x].cishu[y]++;
		}
		else
		{
			a[x].ac[y]=1;
			a[x].zuida[y]=max(a[x].zuida[y],a[x].cishu[y]);
			a[x].cishu[y]=0;
			if(cnm[x][y]==0)
			{
				cnm[x][y]=1;
				zongti[y]++;
			}
		}
	}
	for(int i=1; i<=n; i++)
	{
		bool f=0;
		for(int j=1; j<=m; j++)
		{
			if(a[i].ac[j]==0)
			{
				f=1;
				break;
			}
		}
		if(f==0)
		{
			a[i].zibi=0;
		}
		else
		{
			bool ff=0;
			for(int j=1; j<=m; j++)
			{
				if(a[i].ac[j]==1||a[i].cishu[j]!=0)
				{
					ff=1;
					break;
				}
			}
			if(ff==0)
			{
				a[i].zibi=998244353;
			}
			else
			{
				bool fff=0;
				for(int j=1; j<=m; j++)
				{
					if(a[i].ac[j]==1)
					{
						fff=1;
						break;
					}
				}
				if(fff==0)
				{
					a[i].zibi=1000000;
				}
				else
				{
					for(int j=1; j<=m; j++)
					{
						a[i].zuida[j]=max(a[i].zuida[j],a[i].cishu[j]);
						if(zongti[j]!=0&&a[i].ac[j]==0)
						{
							a[i].zibi+=20;
							if(zongti[j]>=n/2) a[i].zibi+=10;
						}
						if(a[i].ac[j]==0)
						{
							a[i].zibi+=a[i].zuida[j]*a[i].zuida[j]*2;
						}
						else
						{
							a[i].zibi+=a[i].zuida[j]*a[i].zuida[j];
						}
					}
				}
			}
		}
	}
	for(int i=1; i<=n; i++) cout<<a[i].zibi<<endl;
	return 0;
}

7-14 6N. 合并!

有 n 个石子堆,第 i 个在一开始有 a
​i
​​ 个石子。

每次你可以选择两堆相邻的石子 a
​i
​​ ,a
​i+1
​​ 合并,合并完之后得到的收益为 a
​i
​​ a
​i+1
​​ ,两堆石子变成一堆有 a
​i
​​ +a
​i+1
​​ 个石子的堆。

你会一直进行合并操作,直到只剩下一堆石子为止。

求你能得到的最大的收益之和。

输入格式
第一行一个正整数 n

第二行 n 个正整数,表示 a
​1…n
​​

1≤n,a
​i
​​ ≤2000

输出格式
输出一个数,表示能获得的最大收益

输入样例
3
1 2 3

输出样例
11

思路

这题太坑了~好吧,还是自己菜。
一开始还在想高级算法,区间DP之类的,之后才发现,随便怎么合并,答案一样。那就搞就完了。

#include<iostream>
using namespace std;
long long a[2005];
int main()
{
	long long n;
	cin>>n;
	for(long long i=1; i<=n; i++)
	{
		cin>>a[i];
	}
	long long ans=0;
	long long c=a[1]+a[2];
	for(long long i=1; i<n; i++)
	{
		ans+=a[i]*a[i+1];
		a[i+1]=a[i]+a[i+1];
	}
	cout<<ans<<endl;
	return 0;
}

7-3 6C. 酒馆战棋

酒馆战棋是一个很有趣的游戏,这个游戏一共有两名玩家,每个玩家各自控制一定数量的随从,随从从左往右排列。

随从有两个基础属性:攻击力的血量,两个随从交战后会受到对方攻击力的伤害(失去对应的血量),当一个随从的血量降到 0 或者以下时会死亡。

整个对战流程如下:你的随从从左往右依次进行攻击,每次攻击是随机选择一个可以攻击的对方随从进行攻击。而对方的随从全程不动。

由于对方开了外挂,所以他的随从的血量和攻击力都是无穷大,而你的随从的攻击力和血量都是 1,但是随从除了攻击力和血量以外可能还有一些其他的属性:

  1. 圣盾:圣盾可以抵挡一次伤害,抵挡伤害后圣盾会消失。

  2. 剧毒:拥有剧毒属性的随从攻击力视为无穷大(由于你的随从的攻击力都是 1,所以只有剧毒属性才能击杀对方的随从)。

  3. 嘲讽:当对方场上有带有嘲讽的随从时,你只能攻击有嘲讽的随从。

现在已知你的随从从左往右分别有哪些属性,以及对方各种属性的随从有几个(显然对方随从的顺序并不会影响战斗)。

因为每次攻击是随机选择的,所以出题人经常被系统给演了,所以他想知道在所有情况下最多能消灭几只随从,以及最少能消灭几只随从。

输入格式
第一行一个正整数 T 表示数据组数 (1≤T≤100)

对于每组数据:

第一行五个整数 n,a,b,c,d,表示你的随从个数,以及对方的普通随从,圣盾随从,嘲讽随从,圣盾嘲讽随从的个数。(因为对方的随从的属性都是无限大,所以剧毒对他来说没有意义)

第二行一个长度为 n 的 01 串,第 i 个字符为 1 表示从左往右第 i 个随从是否具有剧毒属性,若有则为 1,否则为 0。(因为只需要关心消灭了几个随从,所以嘲讽和圣盾属性无意义)。

保证 1≤n≤1000,0≤a,b,c,d≤1000

输出格式
对于每组数据输出一行两个整数,表示最多消灭几个随从以及最少消灭几个随从。

输入样例
1
5 2 2 2 1
10101

输出样例
3 2

思路

(做完这题的晚上,我下了炉石,嗯,之后的一周,没补题,但是学会了炉石)

#include<iostream>
using namespace std;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n, pt1, sd1, cf1, sc1, pt2, sd2, cf2, sc2;
		cin>>n>>pt1>>sd1>>cf1>>sc1;
		string a;
		cin>>a;
		pt2=pt1;
		sd2=sd1;
		cf2=cf1;
		sc2=sc1;
		int ans1=0, ans2=0;
		for(int i=0; i<n; i++)
		{
			if(a[i]=='1')
			{
				if(sc1>0)
				{
					if(cf1>0)
					{
						cf1--;
						ans1++;
					}
					else
					{
						cf1++;
						sc1--;
					}
				}
				else if(cf1>0)
				{
					cf1--;
					ans1++;
				}
				else if(pt1>0)
				{
					pt1--;
					ans1++;
				}
				else if(sd1>0)
				{
					sd1--;
					pt1++;
				}
			}
			else
			{
				if(sc1>0)
				{
					sc1--;
					cf1++;
				}
				else if(cf1==0&&sd1>0)
				{
					sd1--;
					pt1++;
				}
			}
			if(a[i]=='1')
			{
				if(sc2>0)
				{
					sc2--;
					cf2++;
				}
				else if(cf2>0)
				{
					cf2--;
					ans2++;
				}
				else if(sd2>0)
				{
					sd2--;
					pt2++;
				}
				else if(pt2>0)
				{
					pt2--;
					ans2++;
				}
			}
			else
			{
				if(sc2>0&&cf2==0)
				{
					sc2--;
					cf2++;
				}
				else if(cf2==0&&pt2==0&&sd2>0)
				{
					sd2--;
					pt2++;
				}
			}
		}
		cout<<ans1<<" "<<ans2<<endl;
	}
}

7-6 6F. 图与三角形

给定一张完全无向图,每条边是黑色或者白色,你需要求有几个同色三角形,也就是有多少 (a,b,c) 满足 1≤a<b<c≤n,且 (a,b),(b,c),(a,c) 同色。

为了防止输入过大,输入用一种奇怪的方式给出,详情见输入描述。

输入格式
第一行一个正整数 n 表示这张图的点数。

接下来第二行五个正整数 A,B,C,P,D,1≤A,B,C,P,D≤10
​9
​​ ,且 D≤P,然后我们规定,对于边 i,j(i<j),如果 (A(i+j)
​2
​​ +B(i−j)
​2
​​ +C) mod P>D,则该边为黑色,否则为白色。

1≤n≤5000

输出格式
输出一个数表示同色三角形个数,注意本题答案不需要取模

输入样例
6
2 3 4 11 5

输出样例
6

思路

完全由队友出的题,说真我自己还没研究。。。

#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
vector<ll> v[5010];
int main()
{
	ll n, a, b, c, p, d, sum=0;
	cin>>n>>a>>b>>c>>p>>d;
	for(int i=1; i<=n; i++)
	{
		for(int j=i+1; j<=n; j++)
		{
			if((a*(i+j)*(i+j)+b*(i-j)*(i-j)+c)%p>d)
			{
				v[i].push_back(j);
				v[j].push_back(i);
			}
		}
	}
	for(int i=1; i<=n; i++)
	{
		sum+=(v[i].size()*(n-v[i].size()-1));
	}
	cout<<n*(n-1)*(n-2)/6-sum/2<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值