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≤10e5).
输出格式
一行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.
输入格式
第一行两个正整数 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),以编号小的规则为准)。
-
如果一个选手没有做出任何提交,则最后他的自闭程度为 998244353
-
如果一个选手没有 AC 任何一道题,则最后他的自闭程度为 1000000
-
如果一个选手 AC 了所有题,则最后它的自闭程度为 0
-
对于一个题,如果该题最后有人通过但这个选手没通过,则这个选手的自闭程度增加 20
-
对于一个题,如果至少 ⌊n/2⌋ 个选手 AC 了这个题,但某个选手没通过,则该选手的自闭程度在规则 4 的基础上再额外增加 10
-
在某个题中,如果该选手连续 WA 了最多 K 次(也是就该题的某 K 次连续提交都是 WA),则该选手自闭程度增加 K
2
-
在某个题中,如果该选手连续 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,所以只有剧毒属性才能击杀对方的随从)。
-
嘲讽:当对方场上有带有嘲讽的随从时,你只能攻击有嘲讽的随从。
现在已知你的随从从左往右分别有哪些属性,以及对方各种属性的随从有几个(显然对方随从的顺序并不会影响战斗)。
因为每次攻击是随机选择的,所以出题人经常被系统给演了,所以他想知道在所有情况下最多能消灭几只随从,以及最少能消灭几只随从。
输入格式
第一行一个正整数 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;
}