ZF_20160614_计蒜客

本文深入讲解了多种竞赛级算法,包括树形DP、容斥原理、KMP算法等,并通过具体题目介绍了这些算法的应用场景及实现技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初赛一 青云的机房组网方案
求所有互质节点距离和
1、树形dp
2、质因数的容斥原理
例如i为24,只需要考虑1,2,3,6
i为45,只需要考虑1,3,5,15

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define clr(x,y) memset(x,y,sizeof x)
using namespace std;
ll pri[510],vis[510],lim=500,pnt;
ll tag[510],tnt,F[510];
vector<ll>Div[510],Mul[510];
void dfs(ll cur,ll id,ll flag)
{
    if(id>pnt)return;
    dfs(cur,id+1,flag);
    if(cur*pri[id]<=lim)
    {
        tag[++tnt]=cur*pri[id];
        F[cur*pri[id]]=-flag;
        dfs(cur*pri[id],id+1,-flag);
    }
}
void getP()
{
    for(ll i=2;i<=lim;i++)
    {
        if(!vis[i])
        {
            pri[++pnt]=i;
            for(ll j=2;i*j<=lim;j++)vis[i*j]=1;
        }
    }
    tag[++tnt]=1;F[1]=1;
    dfs(1,1,1);
    sort(tag+1,tag+tnt+1);
    for(ll i=1;i<=tnt;i++)
    {
        for(ll j=1;j*tag[i]<=lim;j++)
        {
            Div[j*tag[i]].push_back(tag[i]);
            Mul[tag[i]].push_back(j*tag[i]);
        }
    }
}
ll n,dat[10010],ans;
ll tot[510],left[10010][510],right[10010][510];
ll sum[10010][510];
namespace G
{
    vector<ll>V[10010];
    ll mx;
    void Init()
    {
        for(ll i=1;i<=n;i++)V[i].clear();
        clr(tot,0);clr(left,0);clr(sum,0);
        ans=0;mx=0;
    }
    void dfs(ll u,ll fa)
    {
        left[u][dat[u]]++;
        ll len=V[u].size();
        for(ll i=0;i<len;i++)
        {
            ll v=V[u][i];
            if(v==fa)continue;
            dfs(v,u);
            for(ll j=1;j<=mx;j++)left[u][j]+=left[v][j];
        }
        for(ll i=1;i<=tnt;i++)
        {
            for(int j=1;j*tag[i]<=lim;j++)
                sum[u][tag[i]]+=left[u][j*tag[i]];
        }
        for(ll i=1;i<=mx;i++)right[u][i]=tot[i]-left[u][i];
        for(ll i=1;i<=mx;i++)
        {
            ll siz=Div[i].size();
            for(ll j=0;j<siz;j++)
            {
                ans+=sum[u][Div[i][j]]*F[Div[i][j]]*right[u][i];
            }
        }
    }
    void Go()
    {
        while(~scanf("%lld",&n))
        {
            Init();
            for(ll i=1;i<=n;i++)scanf("%lld",&dat[i]),mx=mx>dat[i]?mx:dat[i],tot[dat[i]]++;
            for(ll i=1;i<n;i++)
            {
                ll x,y;
                scanf("%lld%lld",&x,&y);
                V[x].push_back(y);V[y].push_back(x);
            }
            dfs(1,-1);
            printf("%lld\n",ans);
        }
    }
}
int main()
{
    getP();
    G::Go();
}

初赛二 联想的显示屏校准
计算几何

初赛三 百度帐号的选取方案
输入用户名abc,如果重复就变成abcabc,求不重复时循环节最小是多少?

KMP
1、val[i][j] 从i到j的最大循环节
2、num[i][j] i开始循环节为j的子串有几个
3、sum[i][j] num数组的后缀和

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define clr(x,y) memset(x,y,sizeof x)
using namespace std;
ll val[1010][1010],num[1010][1010],sum[1010][1010];
namespace KMP
{
    ll nxt[100010];
    ll gnt(char b[],ll lb)
    {
        for(ll i=0,j=nxt[0]=-1;i<lb;)
        {
            j==-1||b[i]==b[j]?
            nxt[++i]=++j:j=nxt[j];
        }
    }
};
char c[1010];
int main()
{
    while(~scanf("%s",c+1))
    {
        ll len=strlen(c+1);
        clr(val,0);
        clr(num,0);
        clr(sum,0);
        for(ll i=1;i<=len;i++)
        {
            KMP::gnt(c+i,len+1-i);
            for(ll j=i;j<=len;j++)
            {
                val[i][j]=(j-i+1)%((j-i+1)-KMP::nxt[j-i+1])==0?(j-i+1)/((j-i+1)-KMP::nxt[j-i+1]):1;
                num[i][val[i][j]]++;
            }
        }
        for(ll i=len;i>0;i--)
        {
            for(ll j=1;j<=len;j++)
                sum[i][j]=sum[i+1][j]+num[i][j];
        }
        ll ans=0;
        for(ll i=1;i<=len;i++)
        {
            for(ll j=i;j<=len;j++)
                ans+=sum[j+1][val[i][j]];
        }
        printf("%lld\n",ans);
    }
}

初赛四 遗失的支付宝密码
1、容斥 f(x)表示至少含有x个square的字符串的个数
结果为c1*m^1 + c2*m^2+ … + cn*m^n
2、打表 电脑大概需要跑20秒左右
3、要求前缀和,因为字符串长度要求不大于n,1,2也要算
4、用unsigned long long比较好

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll unsigned long long
using namespace std;
const ll mod=(ll)1<<32;
ll tab[50][50]={
{0},
{0,1},
{0,-1,1},
{0,0,-1,1},
{0,1,-1,-1,1},
{0,0,1,-1,-1,1},
{0,0,1,0,-1,-1,1},
{0,0,0,1,0,-1,-1,1},
{0,-1,1,1,0,0,-1,-1,1},
{0,0,-1,1,1,0,0,-1,-1,1},
{0,-1,0,0,2,0,0,0,-1,-1,1},
{0,0,-1,0,0,2,0,0,0,-1,-1,1},
{0,0,-1,-1,1,1,1,0,0,0,-1,-1,1},
{0,0,0,-1,-1,1,1,1,0,0,0,-1,-1,1},
{0,0,-1,0,-1,0,2,0,1,0,0,0,-1,-1,1},
{0,0,0,-1,0,-1,0,2,0,1,0,0,0,-1,-1,1},
{0,2,-3,0,-1,0,0,1,1,0,1,0,0,0,-1,-1,1},
{0,0,2,-3,0,-1,0,0,1,1,0,1,0,0,0,-1,-1,1},
{0,1,0,0,-3,0,-1,1,1,0,1,0,1,0,0,0,-1,-1,1},
{0,0,1,0,0,-3,0,-1,1,1,0,1,0,1,0,0,0,-1,-1,1},
{0,2,-1,0,-1,0,-3,0,0,2,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,2,-1,0,-1,0,-3,0,0,2,0,0,1,0,1,0,0,0,-1,-1,1},
{0,2,1,-1,-1,-1,-1,0,-3,1,1,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,2,1,-1,-1,-1,-1,0,-3,1,1,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,1,1,2,-1,-1,-2,-1,-1,0,-2,2,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,1,1,2,-1,-1,-2,-1,-1,0,-2,2,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,1,2,0,0,1,-1,-2,-2,-1,-1,1,-1,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,1,2,0,0,1,-1,-2,-2,-1,-1,1,-1,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,3,1,0,0,-1,1,-2,-2,-2,-1,0,2,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,0,3,1,0,0,-1,1,-2,-2,-2,-1,0,2,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,2,1,2,0,0,-1,-1,0,-2,-2,-2,0,1,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,0,2,1,2,0,0,-1,-1,0,-2,-2,-2,0,1,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-3,6,1,1,-1,2,0,-1,-1,-2,0,-2,-2,-1,1,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-3,6,1,1,-1,2,0,-1,-1,-2,0,-2,-2,-1,1,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-2,2,1,6,-1,0,-1,2,-1,-1,-2,-2,0,-2,-1,0,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-2,2,1,6,-1,0,-1,2,-1,-1,-2,-2,0,-2,-1,0,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-4,4,0,4,0,5,-2,0,-1,1,-1,-2,-2,-2,0,-1,0,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-4,4,0,4,0,5,-2,0,-1,1,-1,-2,-2,-2,0,-1,0,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-4,4,-2,5,1,2,0,4,-2,0,-2,1,-2,-2,-2,-2,1,0,-1,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,0,-4,4,-2,5,1,2,0,4,-2,0,-2,1,-2,-2,-2,-2,1,0,-1,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1},
{0,-7,7,-3,6,-2,5,0,2,-1,4,-2,-1,-2,0,-2,-2,-2,-1,2,-1,-1,-1,0,0,1,-2,1,0,1,0,0,1,0,1,0,0,0,-1,-1,1}
};
ll pow(ll p,ll q)
{
    ll ret=1;
    while(q)
    {
        if(q&1)
        {
            ret*=p;ret%=mod;
        }
        q>>=1;
        p=p*p%mod;
    }
    return ret;
}
int main()
{
    for(ll i=1;i<=40;i++)
    {
        for(ll j=0;j<=i;j++)tab[i][j]+=tab[i-1][j],tab[i][j]=(tab[i][j]%mod+mod)%mod;
    }
    ll n,m;
    while(~scanf("%lld%lld",&n,&m))
    {
        ll ans=0;
        for(ll j=0;j<=n;j++)ans+=(tab[n][j]*pow(m,j))%mod,ans%=mod;
        printf("%lld\n",ans);
    }
}

初赛五 腾讯的新游戏
勇士挑战恶魔,恶魔有n队,每队mi个
q次换防,i队前几个与j队前几个互换
1、链表实现换防
2、逐一计算攻击每队的所需防御力
3、按照防御力从小到大排序,贪心即可

初赛六
1、树形dp f[u][color]
2、再次考到容斥原理
3、dfs的复杂度是O(n)的!dfs的复杂度是O(n)!的dfs的复杂度是O(n)的!当时没想到这一点,不敢枚举最大最小值

#include<cstdio>
#include<cstring>
#include<vector>
#define ll long long
#define D(x) ((x)>-(x)?(x):-(x))
using namespace std;
const ll inf=1e15;
ll dat[110][2],n;
ll a[110],b[110],top;
namespace G
{
    vector<ll>V[110];
    ll f[110][2];
    void dfs(ll u,ll fa)
    {
        ll len=V[u].size();
        for(ll i=0;i<len;i++)
        {
            ll v=V[u][i];
            if(v==fa)continue;
            dfs(v,u);
        }
        ll node; if(u==1)node=len;else node=len-1;
        if(node==0)
        {
            f[u][0]=dat[u][0];f[u][1]=dat[u][1];return;
        }
        if(node==1)
        {
            ll v;for(ll i=0;i<len;i++)if(V[u][i]!=fa)v=V[u][i];
            f[u][0]=max(dat[u][0]+f[v][0]-(D(dat[u][0]-dat[v][0])+999)/1000*666*u,
                        dat[u][0]+f[v][1]-(D(dat[u][0]-dat[v][1])+999)/1000*666*u);
            f[u][1]=max(dat[u][1]+f[v][0]-(D(dat[u][1]-dat[v][0])+999)/1000*666*u,
                        dat[u][1]+f[v][1]-(D(dat[u][1]-dat[v][1])+999)/1000*666*u);
            return;
        }
        f[u][0]=f[u][1]=-inf;
        ll mx,mi,mxf,mif;
        for(ll i=0;i<len;i++)//第一个点
        {
            ll t1=V[u][i];if(t1==fa)continue;
            for(ll c1=0;c1<2;c1++)
            {
                for(ll j=i+1;j<len;j++)//第二个点
                {
                    ll t2=V[u][j];if(t2==fa)continue;
                    for(ll c2=0;c2<2;c2++)
                    {
                        for(ll c0=0;c0<2;c0++)//u点
                        {
                            if(dat[t1][c1]>dat[t2][c2])
                                mx=dat[t1][c1],mxf=f[t1][c1],mi=dat[t2][c2],mif=f[t2][c2];
                            else
                                mi=dat[t1][c1],mif=f[t1][c1],mx=dat[t2][c2],mxf=f[t2][c2];
                            mx=max(mx,dat[u][c0]);
                            mi=min(mi,dat[u][c0]);
                            ll ret=dat[u][c0]+mxf+mif-(mx-mi+999)/1000*666*u;
                            ll flag=1;
                            for(ll l=0;l<len;l++)if(l-i&&l-j)//其他点
                            {
                                ll t3=V[u][l];if(t3==fa)continue;
                                ll tmp=-inf;
                                if(mi<=dat[t3][0]&&dat[t3][0]<=mx)tmp=max(tmp,f[t3][0]);
                                if(mi<=dat[t3][1]&&dat[t3][1]<=mx)tmp=max(tmp,f[t3][1]);
                                if(tmp==-inf)
                                {
                                    flag=0;break;
                                }
                                else ret+=tmp;
                            }
                            if(flag)f[u][c0]=max(f[u][c0],ret);
                        }
                    }
                }
            }
        }
    }
    void Go()
    {
        while(~scanf("%lld",&n))
        {
            for(ll i=1;i<=n;i++)scanf("%lld%lld",&dat[i][0],&dat[i][1]),V[i].clear();
            for(ll i=1;i<n;i++)
            {
                ll x,y;
                scanf("%lld%lld",&x,&y);
                V[x].push_back(y);
                V[y].push_back(x);
            }
            dfs(1,-1);
            printf("%lld\n",max(f[1][0],f[1][1]));
        }
    }
}
int main()
{
    G::Go();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值