cf补题,更新中
- 记得注意检查括号位置!
- 760~D. Array and Operations
- 760~E. Singers' Tour
- 762~D. New Year's Problem
- 762~E. MEX and Increments
- 763~C. Balanced Stone Heaps
- 764~C. Division by Two and Permutation
- 764~D. Palindromes Coloring
- 765~C. Road Optimization
- 766~C. Not Assigning
- 766~D. Not Adding
- 767~C. Meximum Array
- 767~D. Peculiar Movie Preferences
- 119~E. Replace the Numbers
- 120~C. Set or Decrease
- 121~C. Monsters And Spells
- 2022~C. Hidden Permutations
记得注意检查括号位置!
760~D. Array and Operations
题意:对数组执行
k
k
k次操作,每次任选两个数字(值可以相同,位置不同)删除,并将他们相除的结果插入数组中,计算所能得到的最低分。
题解:将数组进行排序,用低位的除以高位的
(
i
<
j
(i<j
(i<j情况
)
)
),结果只有0,1两种情况,新增数字对数组求和影响最小,最开始想的是用双端队列,头尾相除,然后发现可能有1 3 3 7这样的情况,所以不成立,正确解法是可以将其控制在
(
n
,
n
−
k
+
1
)
(n,n-k+1)
(n,n−k+1)范围内,这样就可以使结果最小。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[200];
int vis[200];
int main() {
int t;
cin>>t;
map<int,int>mp;
deque<int>d;
while(t--)
{
int n,k;
cin>>n>>k;
mp.clear();
d.clear();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
cin>>a[i];
vis[i]=1;
}
sort(a+1,a+n+1);
int j;
int ans=0;
for(int i=n;i>=n-k+1;i--)
{
j=i-k;
vis[i]=0;
vis[j]=0;
if(a[i]==a[j])ans++;
}
// cout<<ans<<" ";
for(int i=1;i<=n;i++)
{
if(vis[i]==1)ans+=a[i];
}
cout<<ans<<endl;
}
}
760~E. Singers’ Tour
题意:相当于围成一个环,第
i
i
i个选手在第
i
i
i处有对应时间
a
i
ai
ai,第
i
+
1
i+1
i+1处有对应时间
2
∗
a
i
2*ai
2∗ai,第
i
+
2
i+2
i+2处有对应时间
3
∗
a
i
3*ai
3∗ai,循环一圈结束,每个选手都如此,所以有如下:
题解:将输入数据求和即为
(
n
+
1
)
∗
n
/
2
(n+1)*n/2
(n+1)∗n/2倍的单次求和结果,可得
(
a
+
b
+
c
+
d
+
e
+
f
)
(a+b+c+d+e+f)
(a+b+c+d+e+f)的结果,在将第二列减第一列加
6
b
6b
6b可以得到
b
b
b的表达式,剩下也可依次推出。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
int a[200010],b[200010],c[200010],ans[200010];
int main() {
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
ll sum=0;
for(int i = 1 ;i<=n ; i++)
{
cin>>a[i];
sum+=a[i];
}
ll ab=sum/((n+1)*n/2);
int flag=0;
if(sum%((n+1)*n/2)!=0){
flag=1;
}
else
{
a[n+1]=a[1];
sum=sum/((n+1)*n/2);
for(int i = 2 ; i<= n+1 ;i++){
ll m=sum+a[i-1]-a[i];
ans[i]=m/n;
if(m%n!=0||m/n<=0)
{
flag=1;
break;
}
}
}
if(flag==1)cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
ans[1]=ans[n+1];
for(int i=1 ; i<= n; i++)
{
cout<<ans[i]<<" ";
}
cout<<endl;
}
}
}
762~D. New Year’s Problem
题意:
n
n
n个朋友,
m
m
m家店,如果第
j
j
j 个朋友收到在商店购买的编号为
i
i
i 的礼物,则该朋友收到 pij个快乐单位,最多访问
n
−
1
n-1
n−1家店,使所有朋友中的最小快乐单位最大。
题解:只能去比朋友个数少1的店,故而有一家店要选两个礼物,分两个数组进行记录,一个数组的行记录一家商店的所有快乐单位,另一个记录一个朋友所能得到的快乐单位,并分别进行排序,因为要使最小单位最大,所以将比较得出所有朋友得到的最大快乐单位的最小值,并将其与所有店铺的第二大快乐单位比较,小的即为答案。
代码:
#include <bits/stdc++.h>
using namespace std;
//#define int long long
int main() {
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>m>>n;
vector< vector<int> > p(n, vector<int>(m) );
vector< vector<int> > q(m, vector<int>(n) );
for(int i=0 ; i < m ;i++)
{
for(int j=0;j<n;j++)
{
cin>>q[i][j];
p[j][i]=q[i][j];
}
}
// for(int i=0;i<n;i++)
// {
// for(int j=0;j<m;j++)cout<<p[i][j]<<" ";
// cout<<endl;
// }
// cout<<endl;
// for(int i=0;i<m;i++)
// {
// for(int j=0;j<n;j++)cout<<q[i][j]<<" ";
// cout<<endl;
// }
// cout<<endl;
for(int i=0;i<m;i++)
{
sort(q[i].begin() , q[i].begin() +n, greater<int>());
}
for(int i=0;i<n;i++)
{
sort(p[i].begin() , p[i].begin() + m, greater<int>());
}
int ans=1e9;
for(int i=0;i<n;i++)//每个朋友所得最大的最小情况
{
ans=min(ans,p[i][0]);
}
int ans1=0;
for(int i=0;i<m;i++)//每家店第二大的最大情况
{
ans1=max(ans1,q[i][1]);
}
int res=min(ans,ans1);
cout<<res<<endl;
}
}
762~E. MEX and Increments
题意:从
0
0
0 到
n
n
n 的每个
i
i
i,是否可以使数组的
M
E
X
MEX
MEX 正好等于
i
i
i,可以输出最小操作数,否则
−
1
-1
−1。
题解:使用
m
p
mp
mp统计每个数字的出现次数,当
m
p
[
i
−
1
]
mp[i-1]
mp[i−1]为
0
0
0时,说明该数字初始不存在,然后判断是否可以创造,如果栈内为空(入栈时间:当
m
p
[
i
−
1
]
>
1
mp[i-1]>1
mp[i−1]>1时,将
m
p
[
i
−
1
]
−
−
mp[i-1]--
mp[i−1]−−,并不断将
i
−
1
i-1
i−1入栈),没有多余的比她小的,则不可以,否则用栈内的数字创造,并将其弹出,故而每个
M
E
X
MEX
MEX的创造需要以前的最小情况和
s
u
m
sum
sum加上当前
m
p
[
i
]
mp[i]
mp[i](因为需要将数字
i
i
i对应的几个
i
i
i都加一)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[200010],ans[200010];
signed main() {
int t;
cin>>t;
map<int,int>mp;
while(t--)
{
int n;
cin>>n;
mp.clear();
stack<int>st;
int sum=0;
for(int i=1 ; i<= n; i++)
{
cin>>a[i];
mp[a[i]]++;
ans[i]=-1;
}
for(int i=0 ; i<=n; i++)
{
if(i>0&&mp[i-1]==0)
{
if(st.size()==0)break;
int k=st.top();
sum+=i-1-k;
st.pop();
}
ans[i]=sum+mp[i];
while(i>0&&mp[i-1]>1)
{
st.push(i-1);
mp[i-1]--;
}
}
for(int i=0;i<= n;i++)cout<<ans[i]<<" ";
cout<<endl;
}
}
763~C. Balanced Stone Heaps
题意:选择一个数字
d
(
0
≤
3
⋅
d
≤
h
i
)
d (0≤3⋅d≤hi)
d(0≤3⋅d≤hi),将第
i
i
i 个堆中的
d
d
d 个石子移到第
(
i
−
1
)
(i−1)
(i−1) 个堆中,将第
i
i
i 个堆中的
2
⋅
d
2⋅d
2⋅d 个石子移动到 第
(
i
−
2
)
( i−2)
(i−2) 堆。在
h
i
hi
hi 减少
3
⋅
d
3⋅d
3⋅d 之后,
h
i
−
1
hi-1
hi−1 增加
d
d
d,
h
i
−
2
hi-2
hi−2 增加
2
⋅
d
2⋅d
2⋅d。变空的堆仍然算作堆。使最小堆的石头数尽可能大。
题解:题意为从前往后挪,但因为增加的计数问题不好计算,改用为从后往前遍历的思想,因而要注意只能使用小于等于当前位置原有个数的石头。利用二分来确定最小堆的最大石头数,当当前位置石头总数大于等于
m
i
d
mid
mid,进行移动
(
(
(需留有
m
i
d
mid
mid大小,且只移动原有的石头
)
;
);
);否则则说明改
m
d
i
mdi
mdi大了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod 1000000007
const int INF=0x3f3f3f3f;
const int N=2e5+10;
int a[N],b[N];
int n;
bool check(int mid)
{
//memset(b,0,sizeof(b));
for(int i=1;i<=n;i++)b[i]=0;
for(int i = n ; i>=3 ; i--)
{
if(a[i]+b[i]>=mid)
{
if(b[i]>=mid)
{
b[i-1]+=a[i]/3;
b[i-2]+=2*(a[i]/3);
}
else
{
int bb=a[i]+b[i]-mid;
b[i-1]+=bb/3;
b[i-2]+=2*(bb/3);
}
}
else return 0;
}
if(a[1]+b[1]<mid||a[2]+b[2]<mid)return 0;
else return 1;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
int maxx=0;
for(int i=1; i<= n ; i++)
{
scanf("%d",&a[i]);
maxx=max(maxx,a[i]);
}
int l=0, r = maxx ;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
764~C. Division by Two and Permutation
题意:可以将元素除以二,问最后是否能构成
1
−
n
1-n
1−n的排列。
题解:最开始想复杂了,这道题不需要排序处理,直接将每个输入的数控制在
1
−
n
1-n
1−n的范围内即可,如果该数已经存在,则继续除2知道该数不存在或者没有它的位置。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod 1000000007
const int INF=1e9;
int a[100],b[100];
int main()
{
int t;
cin>>t;
map<int,int>mp;
while(t--)
{
int n;
cin>>n;
mp.clear();
int cnt=0,f=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]<=n&&mp[a[i]]==0)
{
mp[a[i]]=1;
}
else{
while(a[i]>n)a[i]/=2;
while(mp[a[i]]==1)
{
if(a[i]==1)
{
f=1;
break;
}
a[i]/=2;
}
if(f==0)mp[a[i]]=1;
}
}
if(f==1)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
}
764~D. Palindromes Coloring
题意:用
k
k
k种颜色给字符串上色,所有颜色都要使用,涂有相同颜色的字符可以交换位置,需要使
k
k
k个字符串都是回文,求最短的回文串长度
题解:统计两两可以成对的字符,可以将他们分成
k
k
k分,余下的以及最初落单无法成对的字符大于
k
k
k,则说明至少需要再往每种颜色中加一个字符。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod 1000000007
const int INF=1e9;
int main()
{
int t;
cin>>t;
map<int,int>mp;
string s;
while(t--)
{
mp.clear();
int n,k;
cin>>n>>k;
cin>>s;
for(int i=0;i<n;i++)
{
mp[s[i]-'a']++;
}
int ji=0;
for(int i=0;i<=26;i++)
{
//cout<<mp[i]<<" ";
if(mp[i]%2==1)ji++;
}
//cout<<ji<<" ";
int cnt=(n-ji)/2;
int ans=cnt/k*2;
if((cnt%k)*2+ji>=k)ans++;
cout<<ans<<endl;
}
}
765~C. Road Optimization
题意:道路长
l
l
l公里,沿路初始有
n
n
n 个标志,其中第
i
i
i 个设置了限速
a
i
ai
ai,优化后移除不超过
k
k
k个标志,再不拆除七点的情况下,尽可能的是到达终点的时间减小。
题解:DP,加上终点一共有
n
+
1
n+1
n+1个点,计算出所有删除情况的值,然后去比较到达终点的不同删除情况,
d
p
dp
dp公式为:
d
p
[
i
]
[
k
k
]
=
m
i
n
(
d
p
[
i
]
[
k
k
]
,
d
p
[
j
]
[
k
k
−
1
]
+
v
[
j
]
∗
(
a
[
i
]
−
a
[
j
]
)
)
dp[i][kk]=min(dp[i][kk],dp[j][kk-1]+v[j]*(a[i]-a[j]))
dp[i][kk]=min(dp[i][kk],dp[j][kk−1]+v[j]∗(a[i]−a[j]))
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod 1000000007
const int INF=1e9;
int a[510],v[510];
int main()
{
int n,l,k;
cin>>n>>l>>k;
vector< vector<int> > dp(n+10, vector<int>(n+10) );
for(int i=0;i<=n+1;i++)
{
for(int j=0;j<=n+1;j++)dp[i][j]=INF;
}
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
cin>>v[i];
}
a[n+1]=l;
dp[1][0]=0;
for(int i=1;i<=n+1;i++)
{
for(int j=1;j<i;j++)
{
for(int kk=1 ; kk<= j; kk++)
{
dp[i][kk]=min(dp[i][kk],dp[j][kk-1]+v[j]*(a[i]-a[j]));
}
}
}
int ans=INF;
for(int i=0;i<=k;i++)
{
ans=min(ans,dp[n+1][n-i]);
}
cout<<ans<<endl;
}
766~C. Not Assigning
题意:一棵树,它有
n
n
n 个顶点,编号从
1
1
1 到
n
n
n,边的编号从
1
1
1 到
n
−
1
n-1
n−1。树是无环的连通无向图.素数树是由一条或两条边组成的每条路径的权重都是素数的树。输出每条边的权重或者-1.
题解:dfs,因为素数数的性质,所以不会存在连有三条边及以上的点,因为2是素数中的唯一偶数。在合理情况下,先找出只有一条边的点,然后从当前点开始dfs,注意空间使用。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<pair<int,int>,int>mp1;
vector<vector<int> >v(1e5);
vector<pair<int,int> >p;
//struct node
//{
// int x,y;
//}a[100010];
//int cmp(const node &a,const node &b)
//{
// if(a.x!=b.x)return a.x>b.x;
// return a.y>b.y;
//}
void dfs(int x, int father, int col)
{
int m=v[x].size();
for (int i = 0; i<m; i++)
{
if (v[x][i] ==father)continue;
mp1[{x,v[x][i]}]=col;
mp1[{v[x][i],x}]=col;
dfs(v[x][i], x, ((col == 2) ? 3 : 2));
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
n--;
mp1.clear();
for(int i=0;i<=n+1;i++)v[i].clear();
p.clear();
int f=0;
int a,b;
for(int i=1;i<=n;i++)
{
cin>>a>>b;
//cout<<a[i].x<<" "<<a[i].y<<" "<<mp[a[i].x]<<" "<<mp[a[i].y]<<endl;
v[a].push_back(b);
v[b].push_back(a);
p.push_back({a,b});
}
for(int i=1;i<=n+1;i++)
{
if(v[i].size()>=3)
{
f=1;
// cout<<i<<" "<<v[i].size();
break;
}
}
if(f==1)cout<<"-1"<<endl;
else
{
for(int i=1;i<=n;i++)
{
int y=v[i].size();
if(y==1)
{
dfs(i,0,2);
break;
}
}
int op=p.size();
for(int i=0;i<op;i++)
{
cout<<mp1[p[i]]<<" ";
}
cout<<endl;
}
}
}
766~D. Not Adding
题意:从数组中选择两个元素,使得数组中不存在
g
c
d
(
a
i
,
a
j
)
gcd(ai,aj)
gcd(ai,aj),并将
g
c
d
(
a
i
,
a
j
)
gcd(ai,aj)
gcd(ai,aj) 添加到数组的末尾。每次操作都是在新数组上进行的。可以对数组执行操作的最大次数.
题解:直接莽会T,需要利用
g
c
d
gcd
gcd的性质,最大公约数,一定是在1到数组最大元素之间的,遍历
1
1
1到
m
a
x
x
maxx
maxx,如果能通过存在的数据生成,那么
g
c
d
gcd
gcd就会等于当前数值(因为新添加的数是由部分数字生成,所以已经将可能生成的新数据已经在
g
c
d
gcd
gcd过程中产生)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>v;
map<int,int>mp;
const int m=1e6+10;
int g[m],vis[m];
int main()
{
int n;
cin>>n;
int a;
int maxx=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
vis[a]=1;
maxx=max(maxx,a);
}
int cnt=0;
ll ans=0;
for(int i=1;i<=maxx;i++)
{
for(int j=i;j<=maxx;j+=i)
{
if(vis[j]==1)
{
// cout<<i<<" "<<g[i]<<" "<<j<<endl;
g[i]=__gcd(g[i],j);
}
}
if(g[i]==i)ans++;
}
cout<<ans-n<<endl;
}
767~C. Meximum Array
题意:将数组
a
a
a 的前
k
k
k 个数字的
M
E
X
MEX
MEX 附加到数组
b
b
b 的末尾,并将它们从数组
a
a
a 中删除,同时他希望新数组
b
b
b 是字典序上的最大值。通过优化构造数组可以创建的最大数组
b
b
b 是多少。
题解:因为每次推入
b
b
b的
m
e
x
mex
mex都是当前数组所能取得的最大的
m
e
x
mex
mex,所以每次推入一个数字,出现次数加一,当该数字出现的次数已经等于它的总数(后面不会再出现跟他一样大的数,有比他大的也因为出现断层没有影响),就可以将该书推入
b
b
b数组。
s
e
t
.
c
o
u
n
t
:
set.count:
set.count:返回元素在集合中出现的次数
(
0
/
1
)
(0/1)
(0/1)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
bool vis[200010];
int b[200010],c[200010],a[200010];
int main() {
int t;
cin>>t;
vector<int> v;
set<int>s;
while(t--)
{
int n;
cin>>n;
v.clear();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
cin>>a[i];
c[a[i]]++;
}
int mex=0;
for(int i=1;i<=n;i++)
{
s.insert(a[i]);
b[a[i]]++;
while(s.count(mex))mex++;
if(b[mex]==c[mex])
{
v.push_back(mex);
mex=0;
s.clear();
}
}
int m=v.size();
cout<<m<<endl;
for(int i=0;i<m;i++)cout<<v[i]<<" ";
cout<<endl;
}
}
767~D. Peculiar Movie Preferences
题意:判断是否存在子串按顺序连接可以构成回文。
题解:一个字符串长度最多为三,所以回文所需字母最少情况:长度为一(回文);长度为二(前后相同),长度为三(前后相同);;两个长度为二;先选取一个长度为二的在选取一个长度为三的(ABXBA);或者先选取长度为三的在选取长度为二的(同上);三加三。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
int vis[100010];
int main() {
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
//memset(vis,,sizeof(vis));
map<string,int>mp,mp1;
vector<string>v;
int flag=0;
for(int i=1;i<=n;i++)
{
string a;
cin>>a;
int m=a.size();
if(m==1)flag=1;
else if(m==2&&a[0]==a[1])flag=1;
else if(m==3&&a[0]==a[2])flag=1;
else if(flag==0)
{
v.push_back(a);
}
}
//cout<<flag<<" ";
if(flag==0)
{
int p=v.size();
for(int i=0;i<p;i++) {
int x=v[i].size();
string s=v[i];
if(x==2)
{
reverse(s.begin(),s.end());
if(mp[s]==1)
{
flag=1;
break;
}
}
else
{
reverse(s.begin(),s.end());
if(mp[s]==1||mp[s.substr(0,2)]==1)//cb abc
{
flag=1;
break;
}
}
mp[v[i]]=1;
}
mp.clear();
for(int i=p-1;i>=0;i--)
{
string f=v[i];
int k=v[i].size();
if(k==3)//abc ba
{
reverse(f.begin(),f.end());
if(mp[f]==1||mp[f.substr(1,2)]==1)
{
flag=1;
break;
}
}
mp[v[i]]=1;
}
}
if(flag==1)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
119~E. Replace the Numbers
题意:有一个最初为空的数组,执行
q
q
q次查询
,
1
x
,1 x
,1x — 将元素
x
x
x 添加到数组的末尾;
2
x
y
2 xy
2xy — 用
y
y
y 替换数组中所有出现的
x
x
x。
题解:先将所有输入储存起来,当为1时,将
q
.
f
i
r
s
t
q.first
q.first置空。然后从后往前遍历同时使用
m
p
mp
mp来记录它的数据变化,当
q
[
i
]
.
f
i
r
s
t
=
0
q[i].first=0
q[i].first=0说明此时在插入新数据,将
m
p
[
q
[
i
]
.
s
e
c
o
n
d
]
mp[q[i].second]
mp[q[i].second]推入,
m
p
[
q
[
i
]
.
s
e
c
o
n
d
]
mp[q[i].second]
mp[q[i].second]即为从正序看他插入后的最终显示结果,否则
m
p
[
q
[
i
]
.
f
i
r
s
t
]
=
m
p
[
q
[
i
]
.
s
e
c
o
n
d
]
mp[q[i].first]=mp[q[i].second]
mp[q[i].first]=mp[q[i].second]
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<pair<int,int>>q;
vector<int>ans;
map<int,int>mp;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int a;
cin>>a;
q.push_back({0,0});
if(a==1)cin>>q.back().second;
else cin>>q.back().first>>q.back().second;
}
for(int i=n-1 ; i>=0; i--)
{
if(mp[q[i].second]==0)mp[q[i].second]=q[i].second;
if(q[i].first==0)ans.push_back(mp[q[i].second]);
else mp[q[i].first]=mp[q[i].second];
//cout<<i<<" "<<q[i].first<<" "<<q[i].second<<" "<<mp[q[i].first]<<" "<<mp[q[i].second]<<endl;
}
int x=ans.size();
for(int i = x-1 ; i>=0 ; i--)
{
cout<<ans[i]<<" ";
}
cout<<endl;
}
120~C. Set or Decrease
题意:将
a
i
−
1
ai-1
ai−1或令
a
i
=
a
j
ai=aj
ai=aj,求使数组和小于等于
k
k
k的最小操作次数。
题解:排序求前缀和,得公式
x
x
x为-1次数,
y
y
y为另一操作次数,
a
a
a为排序后的第一个数
(
a
−
x
)
∗
(
j
+
1
)
+
s
u
m
(
2
,
n
−
j
)
<
=
k
(a-x)*(j+1)+sum(2,n-j)<=k
(a−x)∗(j+1)+sum(2,n−j)<=k整理移项即可得到
x
x
x的表达式,遍历
j
j
j,不断更新最小值即可。
代码:
121~C. Monsters And Spells
题意:
n
n
n 个怪物,每个对应有
h
i
hi
hi 生命值,分别在
k
i
ki
ki是出现,所有的
k
k
ki都不一样,施法所需的法力值是所有施法法术的法力使用量之和。求杀死所有怪物所需的最少法力值。
题解:WA了2发才过。。。求杀死所有怪物的最小值其实就是在保证杀死怪物的情况下不发生浪费,有两种情况,一种是杀死他所需要的
k
i
ki
ki在杀死上一个怪物后有足够的时间酝酿,此时法力值就是伤害值,另一种情况即为在杀死上一个怪物后没有足够的从1开始的时间,只能接着上面的伤害接着加,此时需要注意也许会存在从上一个怪物的伤害开始还不够的情况,所以需要在一定范围内遍历,求最大值。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[110],b[110],c[110];
int main()
{
int t;
cin>>t;
map<int,int>mp;
while(t--)
{
int n;
cin>>n;
mp.clear();
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
c[0]=0;
a[0]=0;
for(int i=1;i<=n;i++)
{
if(b[i]<=a[i]-a[i-1])b[i]=b[i];
else
{
ll y=0;
for(int j=1;j<i;j++)
{
if(a[j]>a[i]-b[i])
{
mp[j]=1;
b[i]=max(b[i],b[j]+a[i]-a[j]);
}
}
// if(y!=0)b[i]=b[y]+a[i]-a[y];
// else b[i]=b[i]+b[i-1];
}
}
ll ans=0;
for(int i=1;i<n;i++)
{
//cout<<mp[i]<<" "<<b[i]<<endl;
if(mp[i]!=1)
{
ans+=(b[i]+1)*b[i]/2;
}
}
ans+=(b[n]+1)*b[n]/2;
cout<<ans<<endl;
}
}
2022~C. Hidden Permutations
题意:猜测
p
p
p,最初
q
q
q是一个1~
n
n
n的排列,每次查询均会使得所有的 qi=qpi,最多进行
2
n
2n
2n次查询。
题解:使用并查集,如图所示:
1,3,4和2,5分别成为了两部分,用
m
p
mp
mp进行记录,当
m
p
mp
mp没有记录这个数字时,说明这是一个新的部分,进入循环,不断记录记录每个数的结果。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define mod 1000000007
const int INF=0x3f3f3f3f;
int father[10010];
int find(int u) {
return u == father[u] ? u : father[u] = find(father[u]);
}
int q(int j)
{
cout<<"? "<<j<<endl;
int now;
cin>>now;
return now;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(NULL);
int t;
cin>>t;
map<int,int>mp;
while(t--)
{
int n,cnt=0;
cin>>n;
mp.clear();
for(int i=1 ; i<= n ; i++)father[i]=i;
for(int j=1 ; j<= n ; j++)
{
if(mp[find(j)]!=0)continue;
if(cnt==n)break;
int next,now=q(j);
while(mp[now]==0)
{
next=q(j);
mp[now]=next;
father[find(now)]=find(next);
now=next;
cnt++;
if(cnt==n)break;
}
}
cout<<"! ";
for(int i = 1 ; i <= n;i++)
{
cout<<mp[i]<<" ";
}
cout<<endl;
}
}