101 Hack 44

来源:互联网 发布:锥形皮带轮数据 编辑:程序博客网 时间:2024/06/05 16:09

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-8typedef 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-8typedef 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-8typedef 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,endtypedef 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;        }    } }
0 0
原创粉丝点击