A
题意
T组样例
给你n个数,经过任意操作次数之后使得序列sum最大
每次的合法操作:
随便选ai,aj (aj需要是2的倍数),使ai * =2,aj / =2;
思路
因为数据范围极小,所以直接暴力
代码
const int MAXN=1e5+50;
const ll INF=0x3f3f3f3f;
int n,m;
ll a[MAXN];
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll ans=0;
for(int i=1;i<=n;i++) {
ll tmp = 0;
for(int j=1;j<=n;j++){
if(i==j)continue;
else {
while(a[j]%2==0){
a[j]>>=1;
a[i]<<=1;
}
}
}
for(int j=1;j<=n;j++){
tmp+=a[j];
}
ans=max(ans,tmp);
}
cout<<ans<<endl;
}
B
题意
给定长度为n的字符串s,q次询问,将s[pos]改为tmp之后,最少替换几个字符就可以使子串中不出现“abc”
思路
一开始先预处理出来字符串s的最少替换次数,
易得,一个位置改完最多影响pos-2 ~ pos+2
所以小范围暴力分类讨论即可
代码
const int MAXN=1e5+50;
const ll INF=0x3f3f3f3f;
int n,q;
ll a[MAXN];
char s[MAXN];
void solve(){
cin>>n>>q;
int cnt=0;
cin>>(s+1);
for(int i=1;i<=n-2;i++){
if(s[i]=='a'&&s[i+1]=='b'&&s[i+2]=='c'){
cnt++;
}
}
while(q--){
int pos;char tmp;cin>>pos>>tmp;
int ct1=0;
if(s[pos-2]=='a'&&s[pos-1]=='b'&&s[pos]=='c'){
ct1++;
}
if(s[pos-1]=='a'&&s[pos]=='b'&&s[pos+1]=='c'){
ct1++;
}
if(s[pos]=='a'&&s[pos+1]=='b'&&s[pos+2]=='c'){
ct1++;
}
s[pos]=tmp;
int ct2=0;
if(s[pos-2]=='a'&&s[pos-1]=='b'&&s[pos]=='c'){
ct2++;
}
if(s[pos-1]=='a'&&s[pos]=='b'&&s[pos+1]=='c'){
ct2++;
}
if(s[pos]=='a'&&s[pos+1]=='b'&&s[pos+2]=='c'){
ct2++;
}
cnt+=(ct2-ct1);
cout<<cnt<<endl;
}
}
C
题意
T组样例
给定n和e,
问存在多少对<i,k>使得
a[i] * a[i+e] * … * a[i + k*e] 是个质数
思路
很显然,需要满足这个条件的话,
只有这些数中只有1个质数 和 大于等于1个的 1
首先,欧拉筛预处理出来1e6内所有的质数
然后,计算每个位置(包括当前位置)的左边和右边两侧的连续的间隔为e的1的数量
计算每个质数的贡献
ans+= (l[i-e]+1)*(r[i+e]+1)-1
注意处理边界细节
代码
const int MAXN=2e5+50;
const ll INF=0x3f3f3f3f;
const int maxn=1000000;
bool number[maxn+5];//number[x]==true 即number为素数
int prime[maxn+5];//prime 素数集
int cnt=0;//cnt记录素数个数 从0开始
void isprime(){
memset(prime,0,sizeof(0));
memset(number,true,sizeof(number));
number[1]=false;number[0]=false;
for(int i=2;i<=maxn;i++){
if(number[i])
prime[cnt++]=i;
for(int j=0;j<cnt&&prime[j]*i<=maxn;j++){
number[prime[j]*i]=false;
if(i%prime[j]==0) //保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次
break;
}
}
}
int n;
int e;
int a[MAXN];
int vis[MAXN];
int l[MAXN],r[MAXN];
void solve() {
cin >> n >> e;
for (int i = 1; i <= n; i++) {
vis[i] = 0;
cin >> a[i];
if (a[i] == 1) {
vis[i] = 1;
}
if (number[a[i]]) {
vis[i] = 2;
}
}
for(int i=1;i<=n;i++){
if(vis[i]==1){
if(i-e>=0)l[i]=l[i-e]+1;
else {
l[i]=1;
}
}
else {
l[i]=0;
}
}
for(int i=n;i>=1;i--){
if(vis[i]==1){
if(i+e<=n){
r[i]=r[i+e]+1;
}
else {
r[i]=1;
}
}
else {
r[i]=0;
}
}
ll ans=0;
for(int i=1;i<=n;i++){
if(vis[i]==2){
ll l1,rr;
if(i<e)l1=0;
else l1=l[i-e];
if(i>n-e)rr=0;
else rr=r[i+e];
ans+=(l1+1)*(rr+1)-1;
}
}
cout<<ans<<endl;
}
D
题意
n个点,m条限制
第i条限制表示,你需要用i条边 在所有xi 与 yi 联通的情况下,得到的最大连通块中的点数
思路
易得,如果当前这条限制u,v已经联通,那么我们就可以多出一条边去连通最大的两个联通块。
由于,n=1000,所以可以直接eng暴力
(这题比上一题简单多了 自闭.JPG)
代码
const int MAXN=1e3+50;
const ll INF=0x3f3f3f3f;
int n,m;
int maxn1=0,maxn2=-1;
int fa[MAXN];
int cnt[MAXN];
void init(){
for(int i=1;i<=n;i++){
cnt[i]=1;
fa[i]=i;
}
}
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void join(int x,int y){
x=find(x),y=find(y);
if(x==y)return ;
fa[y]=x;
cnt[x]+=cnt[y];
if(cnt[x]>maxn1){
maxn2=maxn1;
maxn1=cnt[x];
}
else if(cnt[x]>maxn2){
maxn2=cnt[x];
}
}
vector<int>tmp;
void solve() {
cin>>n>>m;
init();
int k=1;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
int flag=find(u)==find(v)?1:0;
k+=flag;
join(u,v);
int ans=0;
tmp.clear();
for(int i=1;i<=n;i++){
int fi=find(i);
if(fi==i){
tmp.pb(cnt[fi]);
}
}
sort(tmp.begin(),tmp.end(),greater());
for(int i=0;i<k;i++){
ans+=tmp[i];
}
cout<<ans-1<<endl;
}
}
VP复盘
思维还是太弱了,C卡了一万年,想了很久会不会是尺取或者二分,哭哭,读题也很弱,D看了好久好久才看懂。。