题解

1.Coin

大意:一个数列,两个人随机从两端取走数。求先手期望取走多少数。
60 分:考场上俺就是这么打的。如果用 f[i][j] 表示数列 [i…j] 先手期望取走的数。

f[i][j]=

(f[i+2][j]+w[i]

+f[i][j-2]+w[j]

+f[i+1][j-1]+w[i]

+f[i+1][j-1]+w[j])/4

O(n^2*T);
100 分: 考虑到对于出现的一定长度的数列,每个位置上的数被取到的概率是不变的。所以如果 f[i][j] 表示出现了长度为 i 的数列,第 j 个数被取到的概率。则:

f[i][j]=

(1-f[i-1][j]+

1-f[i-1][j-1])/2

(其中前者表示取走最后一个数,后者表示取走前一个数。)
这样就可以 O(n^2+n*T);

#include <cstdio>
#define MX 1001
using namespace std;
double f[MX][MX],a,ans;
int t,n;
int main()
{
    freopen("coin.in","r",stdin),freopen("coin.out","w",stdout);
    scanf("%d",&t);
    for(int i=1;i<MX;i++)for(int j=1;j<=i;j++)f[i][j]=1-(f[i-1][j]+f[i-1][j-1])/2;
    for(int i=1;i<=t;i++,ans=0){
        scanf("%d",&n);
        for(int j=1;j<=n;j++){
            scanf("%lf",&a);
            ans+=f[n][j]*a;
        }
        printf("%.3f\n",ans);
    }
    return 0;
}

2.Triangle

大意:求一个图中三元环的个数及不图中三元环的个数。
30 分:枚举每个点暴力判边。
72 分:前 30 分用 30 分做法,后 42 分只输出原图三元环个数,用后文的 O(M) 算法。
100 分:令图中有的边为黑边,没有边为白边。则要求的是全为白边和全为黑边的三元环的个数。令 B 表示全黑三角形,W 为全白三角形,A 表示彩色三角形。则

B+W+A=N(N-1)(N-2)/6

其中 A 可以用 O(N) 求出:设 black[i] 表示与 i 点相连的点数,white[i] 表示与 i 点不相邻的点数,则 A=(Σ(black[i]*white[i]))/2 (i∈V).
由于白边数量较多,所以考虑求出 B 再推出 W。
如果枚举每个点 i, 设 book[j]=1((i,j)∈E),再枚举每个点 j((i,j)∈E),枚举每个点 w((j,w)∈E),如果 book[w]==1,则 B 自加 1。由于这样求出的每个三角形被枚举了 6 次 (A(3,3)=3!),所以

W=N(N-1)(N-2)/6 – (Σ(black[i]*white[i]))/2 – (Σi,j,k∈V 且 (i,j),(i,k),(j,k)∈E1)

这样均摊复杂度为 O(M).
至此,B,W 已经求出。

#include <bits/stdc++.h>
#define MX 100001
#define MD 4323333
using namespace std;
template<typename T>
void in(T &x){
    x = 0;char ch = getchar();
    while(ch >'9' || ch <'0') ch = getchar();
    while(ch<='9' && ch >='0') x = x*10+ch-'0',ch = getchar();
}

long long pbl[MX],pwt[MX],n,m;
long long fst[MX*2],nxt[MX*2],lnum=-1,u[MX*2],v[MX*2];
long long tot,have;

int book[MX];

inline void addeg(int nu,int nv)
{
    lnum++;
    u[lnum]=nu;
    v[lnum]=nv;
    nxt[lnum]=fst[nu];
    fst[nu]=lnum;
}

void work()
{
    long long yuan=0;
    for(register int i=1;i<=n;i++)
    {
        for(register int w=fst[i];w!=-1;w=nxt[w])book[v[w]]=i;
        for(register int w=fst[i];w!=-1;w=nxt[w])
            if(v[w]>i)
                for(register int p=fst[v[w]];p!=-1;p=nxt[p])
                    if(book[v[p]]==i&&v[p]>v[w])yuan++;
    }
    printf("%I64d %I64d\n",yuan,tot-have-yuan);
}

int main()
{
    memset(fst,0xff,sizeof(fst));
    freopen("triangle.in","r",stdin),freopen("triangle.out","w",stdout);
    in(n);in(m);
    for(long long i=1,a,b;i<=m;i++)
    {
        in(a);in(b);
        addeg(a,b),addeg(b,a);
        pbl[a]++,pbl[b]++;
    }
    for(int i=1;i<=n;i++)pwt[i]=n-1-pbl[i];
    tot=n*(n-1)*(n-2)/6;
    for(int i=1;i<=n;i++)have+=pbl[i]*pwt[i];
    have/=2;
    work();
    return 0;
}

3.Aqnum
大意:求 1 到 upto 中有几个数满足:每个质因数小于等于 maxprime 且每种质因数个数为奇数。
30 分:暴力搜索,用 f(x,k) 当前的数为 x, 在处理第 k 个质数,则:继续搜索 f(x,k+1), f(xp[k],k+1), f(xp[k]*p[k]2a,k+1). 复杂度约为∏(upto/p[k])

60 分:如果当前的 xp[k]<=upto 且 xp[k]2>upto, 则找到最后一个满足上述条件的 k2, ans+=k2-k+2; 大约可以剪枝 2k 以上的状态数。

#include <bits/stdc++.h>

#define MX 1000001

using namespace std;

long long prm[80000];
long long tab[MX];
long long mp,upto,pnum;

typedef long long ll;

void makep(ll top)
{
    ll now=2;
    pnum++;
    prm[pnum]=2;
    for(;;)
    {
        for(ll i=1;i*now<=top;i++)
        {
            tab[i*now]=1;
        }
        for(ll i=now;i<=top;i++)
        {
            if(tab[i]==0)
            {
                now=i;
                prm[++pnum]=i;
                break;
            }
            if(i==top)return ;
        }
    }
}

ll sch(ll l,ll k)
{
    ll r=pnum,mid;
    while(mid=(l+r+1)>>1,l!=r)if(k*prm[mid]<=upto)l=mid;else r=mid-1;
    return l;
}

ll ans;

void get(ll x,ll now)
{
    if(now>pnum||x*prm[now]>upto){ans++;return;}
    if(x*prm[now]*prm[now]>upto){ans+=sch(now,x)-now+2;return;}
    get(x,now+1);
    x*=prm[now];
    get(x,now+1);
    while(x*prm[now]*prm[now]<=upto)
    {
        x*=prm[now]*prm[now];
        get(x,now+1);
    }
}

int main()
{
    freopen("aqnum.in","r",stdin),freopen("aqnum.out","w",stdout);
    scanf("%I64d%I64d",&upto,&mp);
    makep(mp);
    get(1,1);
    cout<<ans<<endl;
    return 0;
}
分类: 文章

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注