A - Prime Gift
- 题意:给你n个质数,让你求这n个质数能乘起来组成的第K大数(n≤16)
- 做法:这里使用折半查找和二分
- 具体做法
- ①:先把n个质数分为两个集合,最好相邻的分别放入不同集合
- ②:分别求出这两个集数字可能组合相乘得到的值
- ③:再用二分,遍历一下第一个集合中的数字,和第二个集合中可能的小于mid的组合个数,判断一下是否等于k,如果是mid即为解
代码
#include
#define il inline
#define rg register
#define ldb double
#define lst long long
#define rgt register int
#define N 20
#define pb push_back
using namespace std;
const lst Inf=1e18;
il int read()
{
int s=0,m=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();}
while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return m?-s:s;
}
lst n,K;
vector<lst> v[3];
lst p[N],Sz[3];
void Dfs(int rt,int now,lst ss)
{
if(now>n){v[rt].pb(ss),++Sz[rt];return;}
for(lst w=1;;w*=p[now])
{
Dfs(rt,now+2,ss*w);
if((1e18)/p[now]<w*ss)return;
}
}
int main()
{
n=read(),v[1].pb(0),v[2].pb(0);
for(rgt i=1;i<=n;++i)p[i]=read();
sort(&p[1],&p[n+1]),Dfs(1,1,1),Dfs(2,2,1);
sort(&v[1][1],&v[1][Sz[1]+1]);
sort(&v[2][1],&v[2][Sz[2]+1]);
lst le=1,ri=1e18,mid,tot,Ans;K=read();
while(le<=ri)
{
mid=(le+ri)>>1,tot=0;
for(rgt i=1,j=Sz[2];i<=Sz[1]&&j>=1;++i,tot+=j)
while(j&&mid/v[1][i]<v[2][j])--j;
if(tot<K)le=mid+1;
else Ans=mid,ri=mid-1;
}return printf("%lld\n",Ans),0;
}
B - Maximum Value
- 题意:给你n个数字,求这几个数字相互取余的最大值是多少
- 做法:由于对于一个固定的数字取模的话,最大值+ak再取模的结果是一样的,那么这个最大值肯定是ak ~ a*(k+1)之间的最大的哪一个,所以只要枚举所有数字为模值的情况,然后对于每一种情况都找一下再ak ~ a(k+1)区间的最大值是哪一个,模的结果取最大值即可
代码
#include
#include
#include
using namespace std;
const int maxn = 1e6+5;
int N, a[maxn];
int solve (int x) {
int ret = 0, p = x;
while (p < maxn) {
p += x;
int k = lower_bound(a, a + N, p) - a;
if (k == 0) continue;
else k--;
if (a[k] <= x) continue;
ret = max(ret, a[k] % x);
}
return ret;
}
int main () {
scanf("%d", &N);
for (int i = 0; i < N; i++)
scanf("%d", &a[i]);
sort(a, a + N);
int ans = 0;
for (int i = N-1; i >= 0; i--) {
if (ans >= a[i] - 1)
break;
if (i < N - 1 && a[i] == a[i+1])
continue;
ans = max(ans, solve(a[i]));
}
printf("%d\n", ans);
return 0;
}
C - Cinema
- 题意:n个科学家,m部电影,每一个电影都有一个ai,表示这个电影音频语言,bi表示电影的字幕语言,如果一个科学家能会一个电影的音频语言,他就会非常满意,如果会字幕语言,就会比较满意,如果都不会就不满意了,问最后要让非常满意的科学家尽可能多,如果相等,就然比较满意的尽可能多,问最后选什么电影
- 做法:map存下每一个语言科学家有多少,然后根据不同的满意程度来遍历一下所有电影即可,细节代码中有
代码
#include
#include
#include
#include
using namespace std;
#define inf int(0x3f3f3f3f)
#define mod int(1e9+7)
typedef long long LL;
enum{ N = int(2e5 + 10) };
int a[N], b[N], c[N];
map<int, int>qq;
int main()
{
int n, m;
while (~scanf("%d", &n))
{
qq.clear();
for (int i = 0; i < n; i++)
{
scanf("%d", a + i);
qq[a[i]]++;
}
scanf("%d", &m);
int sud = -1, lag = -1, ans = -1;
for (int i = 0; i < m; i++)scanf("%d", b + i);
for (int i = 0; i < m; i++)scanf("%d", c + i);
for (int i = 0; i < m; i++)
{
if (sud < qq[b[i]] || sud <= qq[b[i]] && lag<qq[c[i]])
{
sud = qq[b[i]];
lag = qq[c[i]];
ans = i + 1;
}
}
printf("%d\n", ans);
}
return 0;
}
D - DNA Evolution
- 题意:给你一个只有“T",“A”,"G’,'C’组成的序列,有m个操作,操作1表示把第i个字符改成a,操作2表示给你一个序列e,e无线循环,原序列区间[l,r]再e无限循环序列中和e重合的字符有几个
- 做法:这题目的难点在于设定树状数组的定义,由于对于同一个字符来说,一个再原序列的位置为x,一个为y,如果x%strlen(e)==y%(strlen(e)的话,也就是说再e中的位置取模相同,举个例子,TATA为原序列,TA为e序列,那么第一个T和第二个T的在e的无限循环序列TATATA…中取模位置相同都是0,那么这两个T的匹配结果肯定相同,在这个例子中都是1
- 知道这个之后,在利用e的长度最多为10,我们就可以开出一个可接受大小的树状数组了,把所有的需要的条件包括进去即可,分别为:一个字符是什么,在原序列中的位置是多少,e的长度是多少,该字符在e中位置是多少。
- 即node[4][11][11][maxn];
- 接下来就是一个普通的树状数组了,直接看代码即可
代码
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5+10;
int node[4][11][11][maxn];
int h[110];
char s[maxn], e[11];
int lowbit(int x){
return x&(-x);
}
void add(int ty, int pos, int x, int d){
for(; x < maxn; x+=lowbit(x)){
for(int k = 1; k <= 10; k++){
node[ty][k][pos%k][x] += d;
}
}
}
int sum(int ty, int len,int pos, int x){
int ans = 0;
for(; x > 0 ; x-=lowbit(x)){
ans += node[ty][len][pos][x];
}
return ans;
}
int query(int l,int r,int ty,int len,int pos){
return sum(ty, len, pos, r) - sum(ty, len, pos, l-1);
}
int main(){
int n;
h['A'] = 0; h['T'] = 1; h['C'] = 2; h['G'] = 3;
scanf("%s%d",s,&n);
int len = strlen(s);
for(int i = 0;i < len; i++){
add(h[s[i]], i, i+1, 1);
}
while(n--){
int a, b, c;
scanf("%d",&a);
if(a == 1){
scanf("%d%s",&b,e);
add(h[s[b-1]], b-1, b, -1);
add(h[e[0]], b-1, b, 1);
s[b-1] = e[0];
}
else{
scanf("%d%d%s",&b,&c,e);
len = strlen(e);
int ans = 0;
for(int i = 0; i < len; i++){
ans += query(b, c, h[e[i]], len, (i + b -1)%len);
}
printf("%d\n",ans);
}
}
return 0;
}
E - Yaroslav and Divisors
G - I Hate It
代码
#include
#include
#include
#include
using namespace std;
const int N=200010;
struct point{
int l,r;
int v;
}tr[N*4];
int f[N];
int n,m;
void push_up(int u){
tr[u].v=max(tr[u<<1].v,tr[u<<1|1].v);
}
int query(int u,int l,int r){
int v=0;
if(tr[u].l>=l&&tr[u].r<=r)return tr[u].v;
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)v=max(v,query(u<<1,l,r));
if(r>mid)v=max(v,query(u<<1|1,l,r));
return v;
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
tr[u].v=f[l];
return;
}
int mid=l+r >> 1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
push_up(u);
}
void modify(int u,int x,int v){
if (tr[u].l == x && tr[u].r == x) tr[u].v = v;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
push_up(u);
}
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d",&f[i]);
}
build(1,1,n);
while(m--){
char c;
int a,b;
scanf(" %c%d%d",&c,&a,&b);
if(c=='Q'){
printf("%d\n",query(1,a,b));
}else{
modify(1,a,b);
}
}
}
}
H - Glad You Came
- 题意:给你一个整数数组a[],每个元素初始化为0. 然后进行 m 个操作,更新a[]中的元素,最后输出 (每个元素a[i]*i) 的 异或和;
- 做法:直接暴力用线段树维护一下一个区间的最小值,修改的时候看一下是否v大于最小值,如果小于就不修改,查询的时候直接暴力求出每一个点的值,在乘i异或
代码
#include
#include
#include
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const int MM=1000010;
const int MOD=(1<<30);
unsigned f[MM<<4];
ll a[MM];
unsigned X,Y,Z;
unsigned get()
{
X=X^(X<<11);
X=X^(X>>4);
X=X^(X<<5);
X=X^(X>>14);
unsigned W=X^(Y^Z);
X=Y;
Y=Z;
Z=W;
return Z;
}
void Push(int rt)
{
a[rt]=min(a[rt<<1],a[rt<<1|1]);
}
void update(int L,int R,ll w,int l,int r,int rt)
{
if(a[rt]>=w)
return ;
if(l==r)
{
a[rt]=w;
return ;
}
int mid=(l+r)>>1;
if(L<=mid)
update(L,R,w,l,mid,rt<<1);
if(R>mid)
update(L,R,w,mid+1,r,rt<<1|1);
Push(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L==l&&r==R)
{
return a[rt];
}
ll ans=0;
int mid=(l+r)>>1;
if(L<=mid)
ans=query(L,R,l,mid,rt<<1);
if(R>mid)
ans=query(L,R,mid+1,r,rt<<1|1);
return ans;
}
int main()
{
int ca;
scanf("%d",&ca);
while(ca--)
{
int n,m;
mem(a,0);
scanf("%d%d%d%d%d",&n,&m,&X,&Y,&Z);
for(int i=1; i<=3*m; i++)
{
f[i]=get();
}
for(int i=1; i<=m; i++)
{
int l=min((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
int r=max((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
int v=f[3*i]%MOD;
update(l,r,v,1,n,1);
}
ll sum=0;
for(int i=1; i<=n; i++)
{
ll w=query(i,i,1,n,1);
sum^=(w*i);
}
printf("%lld\n",sum);
}
return 0;
}
I - Creative Snap
代码
- 题意:有2的n次方个基地,每个基地有若干人,现在要毁灭所有基地,有a和b两个值。选择2个及以上基地时,可以将它们一切为二地分别毁灭,如果这里有人,要花费人b长度,如果没人,花费a。求毁灭所有基地的最小花费。
- 做法:直接暴力递归,对于每一个区间只有两种情况,要么不分开直接毁灭,这样根据区间内有无复仇者有两种不同算法,要么分开,这里就直接递归下去即可
#include
#include
#include
using namespace std;
long long n, k, a, b;
long long hero[1000001];
long long solve(long long l, long long r) {
long long num = upper_bound(hero + 1, hero + 1 + k, r) - lower_bound(hero + 1, hero + 1 + k, l);
if (num == 0) return a;
long long ans = (r - l + 1) * b * num;
if (l >= r) return ans;
ans = min(ans, solve(l, (l + r) / 2) + solve((l + r) / 2 + 1, r));
return ans;
}
int main() {
cin >> n >> k >> a >> b;
for (long long i = 1; i <= k; i++) cin >> hero[i];
sort(hero + 1, hero + 1 + k);
cout << solve(1, (1 << n));
}