2021杭电多校(MINIEYE) 第二场 补题

1005 I love string

题意

给n个字符,依次添加进字符串。每次可以添加到当前串的首或者尾。要求最终串的字典序最小。求使得最终串字典序最小的添加方案数。

n<=10^5

解题思路

先考虑字典序最小的字符,找出这些字符的第一次出现的位置,可以发现,这个第一次出现的位置之后的放置方式是唯一的。考虑剩下的部分,依旧是找字典序最小的字符。

最终结果就是相同连续前缀的长度的2的次幂。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e9+7;
int ksm(int x,int y)
{
    int ret=1;
    while (y)
    {
        if (y&1) ret=1ll*ret*x%maxn;
        x=1ll*x*x%maxn;
        y=y>>1;
    }
    return ret;
}
int main()
{
    int t;
    scanf("%d",&t);
    while (t)
    {
        t--;
        int n;
        scanf("%d",&n);
        char s[200020];
        scanf("%s",s+1);
        int ll=0;
        for (int i=1;i<n;i++) if (s[i]!=s[i+1])
        {
            ll=i;
            break;
        }
        if (!ll) ll=n;
        printf("%d\n",ksm(2,ll-1));
    }
}

1010 I love permutation

题意

给两个长度为n的数组a和b,下标从0开始

c[k]=max(a[i]*a[j]) (i&j>=k)

求c[i]的和(0<=i<n)

解题思路

可以把a[i]的值传递到a[j]处,只要j是i二进制下的子集,这样做不会影响答案。

所以对0~n-1按二进制位数排序,将值一直传递即可。

因为有正负数,存一个最大值和一个最小值,注意考虑结果为负数的情况。

代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define LD long double
#define ull unsigned long long
const LL N=5e5+10;
const LL INF=1e18;
LL a,P,b;
int cnt=0;
void init(){
	return;
}

void add(LL &x,LL y){
	x+=y;if(x>=P)x-=P;
}

LL mul(LL x,LL y){
	LL re=0;
	while(y){
		if(y&1) add(re,x);
		add(x,x);y>>=1;
	} 
	return re;
}

inline long long Mul(long long x,long long y){
    long long tmp=(x*y-(long long)((long double)x/P*y+1.0e-8)*P);
    return (tmp+P)%P;
}

LL qpow(LL x,LL y){
	LL re=1,re2;
	while(y){
		if(y&1) {
			re=Mul(re,x);
		}
		x=Mul(x,x);
		y>>=1;
	}
	return re;
}

void MAIN(){
	scanf("%lld%lld",&a,&P);
	b=qpow(a,(P-1)/2);
	if(b==1) {
		puts("0");
	}
	else if(b==P-1) {
		puts("1");
	}
	else{
	//	cout<<a<<" "<<P<<" "<<b<<endl;
		++cnt;
	}
	return;
}
int main(){
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
	init();
	int ttt=1;scanf("%d",&ttt);
	while(ttt--) MAIN();
	//cout<<cnt<<endl;
	return 0;
}

1011 I love max and multiply

题意

给你两个长度为 n 的数组 Ai,Bi ,要你对每个位置 k ,求出 Ck=max Ai×Bj 满足 i & j \geq

然后输出 ∑Ci。

注意:

  1. 题目的两个数组含负数
  2. 下标从 0 开始

解题思路

简单 DP 。

先考虑 i&j≥k 。

( 1 )第一种情况**,k 的所有 1的位置都与 i,j 有重叠。

比如 k=(10101)2, i=(11101)2, j=(10111)2

( 2 ) 第二种,虽然没有完全重叠,但 i&j多出来的那一位 1 使得自己比 k更大。

比如 k=(10101)2, i=(11001)2, j=(11010)2。

显然,让 Ai 和 Bj 分别达到最大或最小(因为有负数)就得到积的最大值

( 1 ) 对第一种情况,我们这样维护(数组 Bi 、最小值维护类似):

记 fa(k)为 i&k=k 的所有 Ai 的最大值,有 fa(k)=max{fa(1<<p|k),Ak}

令 Ck=fa(k)×fb(k),这样选出来的 (i,j) 满足 i&j≥k。

( 2 ) 第二种情况,不难想到,其所有可能其实都包含在了 C(k) 的后缀里了。

例如上面举例的情况,实际上在 k=(11000)2 会被考虑到。

因此,我们只需要再对 Ck 求一个后缀最大值即可。

最后求和再取模。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 6e5 + 5, mod = 998244353, INF = 1e9;
int T; ll f1[N], f2[N], g1[N], g2[N], n, f[N];
int main() {
    scanf("%d", &T);
    while(T--) {
        for(int i = 0; i < (1<<18); i++) {
            f1[i] = f2[i] = -INF;
            g1[i] = g2[i] = INF;
        }
        scanf("%lld", &n);
        for(int i = 0; i < n; i++) {
            scanf("%lld", g1 + i);
            f1[i] = g1[i];
        }
        for(int i = 0; i < n; i++) {
            scanf("%lld", g2 + i);
            f2[i] = g2[i];
        }
        ll res = 0;
        for(int i = n - 1; i >= 0; i--) {
            for(int j = 0; j < 18; j++) {
                if((1 << j) & i) continue;
                int p = i ^ (1 << j);
                f1[i] = max(f1[i], f1[p]);
                f2[i] = max(f2[i], f2[p]);
                g1[i] = min(g1[i], g1[p]);
                g2[i] = min(g2[i], g2[p]);
            }
        }
        for(int i = 0; i < n; i++) {
            f[i] = max(f1[i] * f2[i], g1[i] * g2[i]);
            f[i] = max(f[i], f1[i] * g2[i]);
            f[i] = max(f[i], f2[i] * g1[i]);
        }
        for(int i = n - 2; i >= 0; i--) {
            f[i] = max(f[i], f[i + 1]);
            // printf("%lld ", f[i]);
        }
        // puts("");
        for(int i = 0; i < n; i++) {
            if(f[i] < 0) f[i] = (f[i] - f[i] / mod * mod + mod) % mod;
            else f[i] = f[i] % mod;
            res = (res + f[i]) % mod;
        }
        printf("%lld\n", res);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值