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。
注意:
- 题目的两个数组含负数
- 下标从 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;
}