比赛链接:http://citel.bjtu.edu.cn/vjudge/contest/view.action?cid=314#overview
读题链接:https://vjudge.net/contest/295670
(因vjudge经常崩溃而无法交题,而北交平台偶尔题目乱码,因此用这种形式)
难度:cf 1300~1800原题
A
知识点:模拟
题意:先求出所有数的和sum,然后从左到右找到前缀和大于等于sum的一半即可。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int a[222222];
long long sum=0,s=0,i;
for(i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
}
for(i=0;i<n;i++){
s+=a[i];
if(s*2>=sum)break;
}
cout<<i+1;
}
B
知识点:字符串,暴力枚举
题意:一次操作可添加一个字符,或copy已有字符串(一共只能copy一次)问最少操作数。
解法:由于n很小(只有100),因此可O(n^2)暴力查找最大的a(0~ i)=a(i+1~2i)的相同子串。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
char a[222];
cin>>a;
int i,j,k,m=0;
for(j=1;j<=n/2;j++){
for(k=0;k<j;k++){
if(a[k]!=a[j+k])break;
}
if(k==j)m=max(m,j);
}
cout<<min(n-m+1,n);
}
C
知识点:贪心
题意:从左到右依次可翻转第1个到第i个部分(前缀字符串),或选择不翻转。问怎样操作可让最终字典序最小
解法:显然aaaa…aabb…bb这样的字符串是字典序最小的。遍历整个字符串,遇到a[i]!=a[i+1]的情况翻转前i个即可。最后检查a[n-1],若是a则需要再翻转一次。
#include<bits/stdc++.h>
using namespace std;
int main(){
int i;
char a[1111];
cin>>a;
int jud,z=0;
for(i=0;a[i+1]!='\0';i++){
if(a[i+1]!=a[i]){
cout<<1,jud++;
}
else cout<<0;
cout<<" ";
}
if(a[i]=='a')cout<<"1";
else cout<<"0";
}
D
知识点:树形dp,dfs
题意:对于给定有根树,对每个叶子染色,对于i从1到n,求快乐节点不小于i个的最小染色的颜色数量k。所谓快乐节点指:以该节点为根的子树,所有叶子的颜色不同。
解法:设叶子总数为m。显然,i=n时k为m,i≤p时k为1。这时可以观察到,假设所有叶子的权值为1,每个节点的权值为它所有孩子权值之和,那么这个权值就是该节点作为“快乐节点”的最小染色数。因此可以用一个dp[i]表示第i个节点的权值,然后dfs求所有节点权值,最后排序后依次从小到大输出即可。
#include<bits/stdc++.h>
using namespace std;
vector<int>g[111111];
int dp[111111]={0};
void dfs(int x){
if(g[x].size()==0)dp[x]=1;
for(int i=0;i<g[x].size();i++){
dfs(g[x][i]);
dp[x]+=dp[g[x][i]];
}
}
int main(){
int n,i;
cin>>n;
for(i=2;i<=n;i++){
int x;
cin>>x;
g[x].push_back(i);
}
dfs(1);
sort(dp+1,dp+1+n);
for(i=1;i<=n;i++)cout<<dp[i]<<" ";
}
E
知识点:贪心
题意:相邻两数x和y合并,新数可选择为x-y或y-x。求最终只剩一个数的最大值。
解法:显然,若所有数有负有正,那么sum(abs(a[i]))即为所求。若全为正或全为负,则要牺牲绝对值最小的那个数,最终的sum-2min(abs)即可。
注意要处理n=1的特解。
#include<bits/stdc++.h>
using namespace std;
long long a[555555];
int main(){
long long n,i,j1=0,j2=0,m1=1e10,m2=-1e10,sum=0;
cin>>n;
for(i=0;i<n;i++){
scanf("%lld",&a[i]);
sum+=abs(a[i]);
if(a[i]>=0)j1=1;
if(a[i]<=0)j2=1;
m1=min(m1,a[i]);
m2=max(m2,a[i]);
}
if(n==1){cout<<a[0];return 0;}
if(j1&&j2)cout<<sum;
else if(j1){
cout<<sum-2*m1;
}
else cout<<sum+2*m2;
}
F
知识点:数学,数论
题意:一个循环节为k的循环字符串(由+和-组成)来控制加或减。然后求∑(i=0~n) si*a^(n−i) *b^i
解法:首先可以把前k项求出来。对于长度为n+1的循环和,循环节数量为(n+1)/k。这时可以看到,对于所有的循环节,对应相同位置的项可以组成公比为(b/a)k的等比数列。这样根据项数(即循环节数量)和首项(即前k项,均是第一个循环节中的数),就可以用a0*(qm-1)/(q-1)计算出和了。
计算过程中可以用a的逆元来表示1/a。
要注意特判公比q=1的情况
#include<bits/stdc++.h>
using namespace std;
#define mod 1000000009
long long inv(long long a){
if(a==1)return 1;
return inv(mod%a)*(mod-mod/a)%mod;
}
long long power(long long a,long long b){
long long res=1;
while(b){
if(b&1)res=res*a%mod;
b/=2;
a=a*a%mod;
}
return res;
}
long long c[111111];
char in[111111];
int main(){
int n,a,b,k,i;
cin>>n>>a>>b>>k;
cin>>in;
c[0]=power(a,n);
for(i=1;i<k;i++){
c[i]=c[i-1]*b%mod*inv(a)%mod;
}
for(i=0;i<k;i++){
if(in[i]=='-')c[i]=mod-c[i];
}
long long sum=0;
int chu=(n+1)/k;
long long q=power(b,k)*power(inv(a),k)%mod;
if(q==1){
for(i=0;i<k;i++){
sum=sum+chu*c[i];
}
cout<<(sum+mod)%mod;
return 0;
}
for(i=0;i<k;i++){
sum=(sum+c[i]*(power(q,chu)-1)%mod*inv(q-1)%mod)%mod;
}
cout<<sum;
}