1. 程式人生 > >牛客練習賽8解題報告

牛客練習賽8解題報告

A. 約數個數的和

題目大意:求1~n中每個數的約數個數之和
簡要題解:經典問題,考慮每個數是多少個數的約數,ans=∑n/i

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

int n,ans;

int main()
{
    scanf("%d",&n);
    for (int i=1
;i<=n;i++) ans+=n/i; printf("%d\n",ans); return 0; }

B. 儲物點的距離

題目大意:數軸上有n個點,第i個點和第i+1個點相距s[i],第i個點有xi個物品,m次詢問,求把第l個點到第r個點之間的物品運到第x個點的代價。
簡要題解:字首和記錄一下xi和xi*si即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 200010 #define mod 1000000007 using namespace std; long long a[maxn],b[maxn],s[maxn]; int n,m; int main() { scanf("%d%d",&n,&m); for (int i=2;i<=n;i++) scanf("%lld",&a[i]),a[i]=(a[i]+a[i-1])%mod; for (int i=1;i<=n;i++) scanf("%lld",&b[i]),s[i]=a[i]*b[i]%mod,b[i]=(b[i]+b[i-1
])%mod,s[i]=(s[i]+s[i-1])%mod; while (m--) { int x,l,r; scanf("%d%d%d",&x,&l,&r); long long ans=0; if (x>l && x<r) ans=a[x]*(b[x]-b[l-1])-(s[x]-s[l-1])+(s[r]-s[x])-a[x]*(b[r]-b[x]); else if (x<=l) ans=(s[r]-s[l-1])-a[x]*(b[r]-b[l-1]); else ans=a[x]*(b[r]-b[l-1])-(s[r]-s[l-1]); ans=(ans%mod+mod)%mod; printf("%lld\n",ans); } return 0; }

C. 迴文串的交集

題目大意:給定一個字串,求有多少對迴文子串相交。
簡要題解:補集轉化,求不相交的迴文子串有多少對,則列舉分界線即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 2000010
#define mod 1000000007

using namespace std;

char s[maxn];
int p[maxn],cnt[maxn],L[maxn],R[maxn];
int n,m,ans;

int main()
{
    scanf("%d%s",&n,s+1);
    s[0]='-';s[n+1]='+';
    int mx=0,id=0;
    for (int i=1;i<=n;i++)
    {
        if (mx>i) p[i]=min(mx-i,p[2*id-i]); else p[i]=1;
        while (s[i-p[i]]==s[i+p[i]]) p[i]++;
        if (i+p[i]>mx) mx=i+p[i],id=i;
        ans=(ans+p[i])%mod;
        cnt[i]++;cnt[i+p[i]]--;
        L[i-p[i]+1]++;L[i+1]--;
    }
    mx=0,id=0;
    for (int i=1;i<=n;i++)
    {
        if (mx>i) p[i]=min(mx-i,p[2*id-i]); else p[i]=0;
        while (s[i-p[i]]==s[i+p[i]+1]) p[i]++;
        if (i+p[i]>mx) mx=i+p[i],id=i;
        ans=(ans+p[i])%mod;
        L[i-p[i]+1]++;L[i+1]--;
        cnt[i+1]++;cnt[i+p[i]+1]--;
    }
    ans=1ll*ans*(ans-1)/2%mod;
    for (int i=1;i<=n;i++) cnt[i]=(cnt[i]+cnt[i-1])%mod,L[i]=(L[i]+L[i-1])%mod;
    //for (int i=1;i<=n;i++) printf("%d ",cnt[i]);printf("\n");
    for (int i=n;i>=1;i--) R[i]=(R[i+1]+L[i])%mod;
    for (int i=1;i<n;i++) ans=(ans-1ll*cnt[i]*R[i+1]%mod+mod)%mod;
    printf("%d\n",ans);
    return 0;
}

D. 加邊的無向圖

題目大意:給定一個無向圖,至少求加入多少條邊可使其聯通。
簡要題解:ans=n-聯通塊個數

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 100010

using namespace std;

int n,m,cnt;
int f[maxn];

int find(int i) {return f[i]==i?i:f[i]=find(f[i]);}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        f[find(x)]=find(y);
    }
    for (int i=1;i<=n;i++) if (f[i]==i) cnt++;
    printf("%d\n",cnt-1);
    return 0;
}

E. 集合中的質數

題目大意:給定一個包含n個質數的集合S,求1~m中有多少個數能被至少一個質數整除。
簡要題解:容斥,列舉被選中的質數,注意爆long long。這裡如果用除法,可以避免溢位問題。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

long long a[21],m,ans;
int n;

void dfs(int i,long long now,long long t)
{
    if (i==n) {ans+=m/now*t;return;}
    i++;
    dfs(i,now,t);
    if (log(now)+log(a[i])<log(m)+0.1) dfs(i,now*a[i],-t);
}

int main()
{
    scanf("%d%lld",&n,&m);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    dfs(0,1,1);
    printf("%lld\n",m-ans);
    return 0;
}

F. 重排的迴文串

題目大意:長度為n只包含小寫字母的字串,m組詢問,求[l,r]中有多少個子串可以重排為字串。
簡要題解:注意到是否可以重排為字串,只與字母出現次數有關,具體些,至於每種字母出現次數的奇偶性有關。於是,可以處理出每種字母出現次數奇偶性的字首狀態,狀態數為2^26,最多有O(n)種,這裡可以離散化。離線莫隊,維護當前區間每種狀態的出現次數,以l為右端點的答案數為cnt[S[l]]+cnt[S[l]^(1<<0)]+……+cnt[S[l]^(1<<25)]

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#define maxn 60010

using namespace std;

struct yts
{
    int l,r,id;
}q[maxn];

int a[maxn],b[maxn],r[maxn],cnt[maxn];
char s[maxn];
long long ans,ANS[maxn];
int num,head[maxn*26],nxt[maxn*26],to[maxn*26];
int n,m,block;
map<int,int> mp;

bool cmp(yts x,yts y)
{
    if ((x.l-1)/block!=(y.l-1)/block) return x.l<y.l;
    return x.r<y.r;
}

long long calc(int x)
{
    long long ans=cnt[x];
    for (int p=head[x];p;p=nxt[p]) ans+=cnt[to[p]];
    return ans;
}

void addedge(int x,int y)
{
    num++;to[num]=y;nxt[num]=head[x];head[x]=num;
}

int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    block=(int)sqrt(n);
    for (int i=1;i<=n;i++) a[i]=a[i-1]^(1<<s[i]-'a'),b[i]=a[i];
    b[n+1]=0;
    sort(b+1,b+n+2);
    int Q=unique(b+1,b+n+2)-b-1;
    for (int i=1;i<=Q;i++) mp[b[i]]=i;
    for (int i=0;i<=n;i++) r[i]=mp[a[i]];
    for (int i=1;i<=Q;i++)
      for (int j=0;j<26;j++)
        if (mp.find(b[i]^(1<<j))!=mp.end()) addedge(i,mp[b[i]^(1<<j)]);

    for (int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+m+1,cmp);
    int L=1,R=0;
    for (int i=1;i<=m;i++)
    {
        while (L<q[i].l) cnt[r[L]]--,ans-=calc(r[L++]);
        while (L>q[i].l) ans+=calc(r[--L]),cnt[r[L]]++;
        while (R>q[i].r) cnt[r[R]]--,ans-=calc(r[R--]);
        while (R<q[i].r) ans+=calc(r[++R]),cnt[r[R]]++;
        ANS[q[i].id]=ans+calc(r[q[i].l-1]);
    }
    for (int i=1;i<=m;i++) printf("%lld\n",ANS[i]);
    return 0;
}

題目比較簡單,寫題解主要是為了練一下markdown。以後儘量用markdown來寫部落格。