101 Hack 44

版权声明:完整版Leetcode题解请出门左转https://github.com/cmershen1/leetcode/tree/master/docs https://blog.csdn.net/cmershen/article/details/54582563

A. Picking Numbers(水题)

https://www.hackerrank.com/contests/101hack44/challenges/picking-numbers

题目大意:

给你个数组,问至多有几个数,其中两两相差不到1.

题目分析:

反正数组长度才100,O(n2)随便一搞即可。

#include <bits/stdc++.h>
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define ms(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define mp make_pair
#define INF 0x3f3f3f3f
#define eps 1e-8

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef vector<ll> vl;

const int M = 1e9 + 7;
const double PI = acos(-1.0);

const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;

int main() {
    //RE("in.txt");WR("out.txt");
    int n;
    int a[105];
    cin>>n;
    for (int i = 0; i < n; i++) 
        cin>>a[i];
    sort(a,a+n);
    int ans=0;
    for(int i=0;i<n;i++) {
        int temp=1;
        for(int j=i+1;j<n;j++) {
            if(a[j]-a[i]<=1)
                temp++;
        }
        ans=max(ans,temp);
    }
    cout<<ans<<endl;
}

B.Alice and Bob’s Silly Game(素数统计)

https://www.hackerrank.com/contests/101hack44/challenges/alice-and-bobs-silly-game

题目大意:

有一个集合{1,2,…n},A和B两人轮流从中选一个素数,删去集合中所有该素数的倍数,无法操作者输。问谁赢?

题目分析:

其实就是统计小于n的素数有多少个,之前沈阳赛区有道题可以统计出1e11的,那么本题的1e5自然不在话下,直接套用hdu 5901题模板即可。

#include <bits/stdc++.h>
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define ms(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define mp make_pair
#define INF 0x3f3f3f3f
#define eps 1e-8

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef vector<ll> vl;

const int M = 1e9 + 7;
const double PI = acos(-1.0);

const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 6;

ll f[340000],g[340000],n;
void init(){
    ll i,j,m;
    for(m=1;m*m<=n;++m)f[m]=n/m-1;
    for(i=1;i<=m;++i)g[i]=i-1;
    for(i=2;i<=m;++i){
        if(g[i]==g[i-1])continue;
        for(j=1;j<=min(m-1,n/i/i);++j){
            if(i*j<m)f[j]-=f[i*j]-g[i-1];
            else f[j]-=g[n/i/j]-g[i-1];
        }
        for(j=m;j>=i*i;--j)g[j]-=g[j/i]-g[i-1];
    }
}
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        init();
        if(f[1]&1)
            cout<<"Alice"<<endl;
        else
        {
            cout<<"Bob"<<endl;
        }
    }
    return 0;
}

C.Expected Tree Leaves(概率与期望)

https://www.hackerrank.com/contests/101hack44/challenges/expected-tree-leaves

题目大意:

从一个点1开始,每次随机选择一个点,在其后面连一个点,重复n次。

设叶子节点个数的期望为E,求(En!) mod (1e9+7).

题目分析:

首先证明E=n/2:

设点i是叶子节点的概率为Pi,则

E=i=1nPi

那么点i是叶子节点,当且仅当i+1到n号点都不跟i相连,其概率为
Pi=i1iii+1...n2n1=i1n1

代入E,得E=n/2.
然后就没什么了。

#include <bits/stdc++.h>
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define ms(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define mp make_pair
#define INF 0x3f3f3f3f
#define eps 1e-8

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef vector<ll> vl;

const int M = 1e9 + 7;
const double PI = acos(-1.0);

const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 6;

int main() {
    //RE("in.txt");WR("out.txt");
    ll f[MAXN];
    f[1]=1;
    for(int i=2;i<=100000;i++) {
        f[i]=(f[i-1]*i)%M;
    }
    ll n;
    cin>>n;
    ll x=n*(1e9+8)/2;
    x=x%M;
    cout<<x*f[n]%M<<endl;
}

D.Palindromic Subsets(线段树+组合数计算)

https://www.hackerrank.com/contests/101hack44/challenges/palindromic-subsets

题目大意:

输入n和q,还有一个n长的、由小写字母构成的字符串s,进行如下操作:
* 1 i j t:对s的[i,j]子串的字母循环右移t个(下标从0开始)
* 2 i j:问s的[i,j]子串中,有多少个子串可以重排成回文串(这里没注意,导致本题一直没做出来,蠢死了),下标不同认为是不同的子串
n,q范围均为1e5.

题目分析:

因为是重排,所以子串aab也算是“回文串”,因为可以重排为aba,这点一直没注意到。

因此,我们只需要考虑字母出现的次数,而不用关心字母出现的先后顺序。

对每个[i,j]区间,统计每个字母出现的数量记为数组cnt[0…25],记even[i]表示第i个字母的偶数个子集的数目,odd[i]表示第i个字母的奇数个数的子集的数目。

例如对字符串aaaabbb,则cnt[0]=4,cnt[1]=3.

even[0]=8(0个a有1个,2个a有C(4,2)=6个,4个a有1个)

odd[0]=8(1个a有C(4,1)=4个,3个a有C(4,3)=4个)

同理even[1]=4,odd[1]=4。

根据组合数公式可知,若cnt[i]不为0,则odd[i]=even[i]=2cnt[i]1,否则odd[i]=0,even[i]=1.

接下来讨论答案,首先如果一个数是回文数,那么至多有一个字母出现奇数次.
首先考虑所有字母出现偶数次的情况,那么答案显然就是even[0]even[1]...even[25].
如果有字母出现奇数次,只需让其他的字母出现了偶数次,那么答案就是:

i=025even[0]...even[i1]odd[i]even[i+1]...even[25]

要想处理这个东西,很显然需要跑一下even数组的前缀积和后缀积,然后转化成形如:

i=025pre[i]odd[i]suf[i]

接下来就简单了,cnt数组因为具有区间递归的性质,只需用线段树维护即可。因为只有26个字母,因此计算pre[i]odd[i]suf[i]的过程时间复杂度为O(26logn),然后线段树查点、更新的时间复杂度为O(logn),故整体的时间复杂度为O(qlog2n26),算一下这个值大约为6.5×107,所以是不会超时的。(个人认为题解给的复杂度错误,因为大数乘方取模的复杂度已经是logn了,而且我这个算法在oj上耗时约为0.8秒,按一般经验oj平台每秒钟处理1e8个数据量来算,数量级也是对的上的)

#include <bits/stdc++.h>
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define ms(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define mp make_pair
#define INF 0x3f3f3f3f
#define eps 1e-8

#define LEFT idx<<1,begin,mid
#define RIGHT idx<<1|1,mid+1,end

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef vector<ll> vl;

const int M = 1e9 + 7;
const double PI = acos(-1.0);

const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 6;

int n,q;
char s[123456];
struct node {
    int lazy;
    int cnt[30];
}segtree[1<<18],id;

ll qmod(ll a,ll b,ll c) {
    ll ans=1;
    a=a%c;
    while(b>0) {
        if(b&1)
            ans=(ans*a)%c;
        b=b/2;
        a=(a*a)%c;
    }
    return ans;
}

node merge(node a,node b) {
    node c;
    c.lazy=0;
    for(int i=0;i<26;i++) {
        c.cnt[i]=a.cnt[(i+a.lazy)%26]+b.cnt[(i+b.lazy)%26];
    }
    return c;
}

void apply(int a,int b) {
    segtree[a].lazy+=b;
}

void down(int x) {
    if(segtree[x].lazy) {
        apply(x*2, segtree[x].lazy);
        apply(x*2+1, segtree[x].lazy);
        node c;
        c.lazy=0;
        for(int i=0;i<26;i++) 
            c.cnt[i]=segtree[x].cnt[(i+segtree[x].lazy)%26];
        segtree[x]=c;
    }
}

void build(int idx,int begin,int end) {
    if(begin==end)
        segtree[idx].cnt[s[begin-1]-'a']++;
    else {
        int mid=(begin+end)/2;
        build(LEFT);
        build(RIGHT);
        segtree[idx]=merge(segtree[idx*2],segtree[idx*2+1]);
    }
}

void update(int idx,int begin,int end,int l,int r,int v) {
    if(r<begin || end<l)
        return;
    if(l<=begin && end<=r)
        apply(idx,v);
    else {
        down(idx);
        int mid=(begin+end)/2;
        update(LEFT,l,r,v);
        update(RIGHT,l,r,v);
        segtree[idx]=merge(segtree[idx*2],segtree[idx*2+1]);
    }
}

node query(int idx,int begin,int end,int l,int r) {
    if(r<begin || end<l)
        return id;
    if(l<=begin && end<=r)
        return segtree[idx];
    else {
        down(idx);
        int mid=(begin+end)/2;
        node a=query(LEFT,l,r);
        node b=query(RIGHT,l,r);
        return merge(a,b);
    }
}

void shift(int i,int j,int t) {
    update(1,1,n,i+1,j+1,(26-(t%26))%26);
}

ll solve(int x,int y) {
    ll pre[27],suf[27],even[27],odd[27];
    for(int i=0;i<27;i++) {
        pre[i]=0;suf[i]=0;even[i]=0;odd[i]=0;
    }
    node d=query(1,1,n,x+1,y+1);//得到结果节点
    for(int i=0;i<26;i++) {
        int c=d.cnt[i];
        if(c==0) {
            even[i]=1;
            odd[i]=0;
        }
        else {
            even[i]=odd[i]=qmod(2,c-1,M);//直接用1<<(c-1)肯定会爆ll
        }
    }
    pre[0]=1;suf[26]=1;
    for(int i=1;i<=26;i++)
        pre[i]=(pre[i-1]*even[i-1])%M;
    for(int i=25;i>=0;i--)
        suf[i]=(suf[i+1]*even[i])%M;
    ll ans=suf[0];//不考虑奇数个字母的出现,答案为(even[0]*...*even[25])%M

    for(int i=0;i<26;i++) {
        ll temp=(pre[i]*odd[i])%M;
        temp=(temp*suf[i+1])%M;
        ans=(ans+temp)%M;
    }
    return ans-1;

}

int main() {
    RE("in.txt");WR("out.txt");
    cin>>n>>q;
    cin>>s;
    // cout<<n<<" "<<q<<endl;
    // cout<<s<<endl;
    build(1,1,n);
    while(q--) {
        int op,x,y,t;
        cin>>op;
        if(op==1) {
            cin>>x>>y>>t;
            shift(x,y,t);
        }
        else {
            cin>>x>>y;
            cout<<solve(x,y)<<endl;
        }
    } 
}
阅读更多

没有更多推荐了,返回首页