7.30 Day3 Test

2018. Test

时间:8:00~13:00
期望得分:100+5+5
实际得分:100+5+0

很多人Hash被卡了(写得丑怪谁呢),水了个A班前10 2333.

T1 A.蔡老板分果子(Hash)

题目链接

对下标集合进行Hash。只需要为每个下标随机一个权值即可。
不放心写了双哈希,其实单哈希就够了。异或或者加,随机权值或设定都行。
一个赋随机unsigned long long权值方式:for(int j=1; j<=5; ++j) val[i]=val[i]<<15^rand();

#include <map>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define base1 (786433)
#define base2 (1572869)
typedef long long LL;
typedef unsigned long long ull;
const int N=2e5+5;
const ull P1[7]={31,193,331,769,12289};
const ull P2[7]={131,97,53,389,49157};

int n;
long long Ans;
ull pw1[N],pw2[N],sum1[N],sum2[N];
std::map<ull,int> vis1,vis2;
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
    int Enum,H[N],nxt[N<<1],to[N<<1];
    inline void AddEdge(int u,int v)
    {
        to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
        to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
    }
}T1,T2;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
void DFS1(int x,int f)
{
    sum1[x]=pw1[x]*base1, sum2[x]=pw2[x]*base2;
    for(int v,i=T1.H[x]; i; i=T1.nxt[i])
        if((v=T1.to[i])!=f) DFS1(v,x), sum1[x]+=sum1[v], sum2[x]+=sum2[v];
    ++vis1[sum1[x]], ++vis2[sum2[x]];
}
void DFS2(int x,int f)
{
    static std::map<ull,int>::iterator it1,it2;

    sum1[x]=pw1[x]*base1, sum2[x]=pw2[x]*base2;
    for(int v,i=T2.H[x]; i; i=T2.nxt[i])
        if((v=T2.to[i])!=f) DFS2(v,x), sum1[x]+=sum1[v], sum2[x]+=sum2[v];
    if((it1=vis1.find(sum1[x]))!=vis1.end() && (it2=vis2.find(sum2[x]))!=vis2.end())
        Ans+=(LL)std::min(it1->second,it2->second);//, printf("Add! %d:%d\n",x,it->second);
}

int main()
{
//  freopen("a3.in","r",stdin);

    n=read(); pw1[0]=pw2[0]=1;
    for(int i=1; i<=n; ++i) pw1[i]=pw1[i-1]*base1+P1[i%5]*i, pw2[i]=pw2[i-1]*base2+P2[i%5]*i;;
    for(int i=1; i<n; ++i) T1.AddEdge(read(),read());
    for(int i=1; i<n; ++i) T2.AddEdge(read(),read());
    DFS1(1,1), --vis1[sum1[1]], --vis2[sum2[1]], DFS2(1,1);
    printf("%lld\n",Ans);

    return 0;
}

T2 B.蔡老板送外卖(并查集 最小生成树)

题目链接
我们要求删掉i后的最小生成树的最大边的权值。我们对所有的点一起考虑。
先求出最小生成树。考虑删掉每个点后其余非树边能对它的贡献。
我们存下非树边,对于边(u,v),w=LCA(u,v),假设dep[u]>dep[v],那么有两种情况:
1.w==v,暂且称这条边为返祖边,它会对u->v(w)(不包括u,v)这条链的点贡献合并上下两个连通块的边。
2.w!=v,称这条边为横边,同理会对u->w,v->w(不包括u,v)上的点贡献合并u,v所在连通块的边。
如果把边从小到大插入,比如一些点被较小的返祖边覆盖过,那么更大的返祖边对它显然没什么用。我们跳过之前更新过的一条链,这可以用并查集。
具体是对于当前节点x,x=Get_fa(x),然后合并x与fa[x],再找到fa[x]所在集合的代表节点,合并其与其父节点...
对fa[]更新这条边的答案,u,v不会被计算,但是会被合并,但是没问题,u,v作为父节点会被更新一次。
不清楚的话,画个图就好了。
判断无解可以记每个点被合并连通块的次数就可以了。
对于返祖边有一次次数贡献;对于横边,我们更新完链后将u,v合并,若u,v之前未被合并则对w有一次次数贡献。
每条返祖边只被枚举一次,每条横边相当于只在LCA处枚举一次。复杂度O(mlogn*α(n))。
要注意并查集每次要把深度高的点的fa[]指向深度低的点;跳fa的时候是树上的不要和并查集的混了。

//4297ms    76584kb
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e6+5;

int n,m,Enum,H[N],to[N<<1],nxt[N<<1],cnt,dgr[N],fa[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
    int fr,to,val;
    bool operator <(const Edge &x)const{
        return val<x.val;
    }
}e[N],g[N];

#define AddEdge(u,v) to[++Enum]=v,nxt[Enum]=H[u],H[u]=Enum,to[++Enum]=u,nxt[Enum]=H[v],H[v]=Enum
inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
int Get_fa(int x){
    return x==fa[x]?x:fa[x]=Get_fa(fa[x]);
}
void Merge(int u,int v){
    if(Get_fa(u)!=Get_fa(v)) fa[fa[u]]=fa[v];
}
int Kruskal()
{
    std::sort(e+1,e+1+m);
    for(int i=1; i<=n; ++i) fa[i]=i;
    int res=0,k=1;
    for(int i=1,u,v; i<=m; ++i)
    {
        if(Get_fa(u=e[i].fr)==Get_fa(v=e[i].to)) {g[++cnt]=e[i]; continue;}
        ++k, ++dgr[u], ++dgr[v], AddEdge(u,v);
        fa[fa[u]]=fa[v], res=std::max(res,e[i].val);
//      if(++k==n) return res;//g
    }
    return k==n?res:-1;
} 
namespace HLD//Heavy-Light Decomposition
{
    int fa[N],sz[N],son[N],dep[N],top[N];
    inline int LCA(int u,int v)
    {
        while(top[u]!=top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
        return dep[u]>dep[v]?v:u;
    }
    void DFS1(int x)
    {
        int mx=0; sz[x]=1;
        for(int i=H[x],v; i; i=nxt[i])
            if((v=to[i])!=fa[x])
            {
                fa[v]=x, dep[v]=dep[x]+1, DFS1(v), sz[x]+=sz[v];
                if(sz[v]>mx) mx=sz[v], son[x]=v;
            }
    }
    void DFS2(int x,int tp)
    {
        top[x]=tp;
        if(son[x])
        {
            DFS2(son[x],tp);
            for(int i=H[x],v; i; i=nxt[i])
                if((v=to[i])!=fa[x] && v!=son[x]) DFS2(v,v);
        }
    }
}
using HLD::dep;
using HLD::LCA;

int main()
{
    n=read(), m=read();
    for(int i=1; i<=m; ++i) e[i]=(Edge){read(),read(),read()};
    int MinTree=Kruskal();
    if(MinTree==-1) return printf("%d\n",-n),0;

    HLD::DFS1(1), HLD::DFS2(1,1);

    static int tot[N],Ans[N];
    for(int i=1; i<=n; ++i) fa[i]=i;
    for(int i=1,u,v,tu,tv,w,val; i<=cnt; ++i)
    {
        if(dep[u=g[i].fr]<dep[v=g[i].to]) std::swap(u,v);
        w=LCA(u,v), val=g[i].val;
        if(v==w)
        {
            u=Get_fa(u);
            for(int p=HLD::fa[u]; dep[p]>dep[v]; u=Get_fa(u), p=HLD::fa[u])
                Ans[p]=val, ++tot[p], Merge(u,p);
        }
        else
        {
            u=Get_fa(tu=u), v=Get_fa(tv=v);
            for(int p=HLD::fa[u]; dep[p]>dep[w]; u=Get_fa(u), p=HLD::fa[u])
                Ans[p]=val, ++tot[p], Merge(u,p);
            for(int p=HLD::fa[v]; dep[p]>dep[w]; v=Get_fa(v), p=HLD::fa[v])
                Ans[p]=val, ++tot[p], Merge(v,p);
            if((u=Get_fa(tu))==(v=Get_fa(tv))) continue;
            Ans[w]=val, ++tot[w];
            if(dep[u]<dep[v]) std::swap(u,v);
            fa[fa[u]]=fa[v];
        }
    }
    long long res=0;
    for(int i=1; i<=n; ++i)
        res += dgr[i]==1?MinTree:(tot[i]<dgr[i]-1?-1:std::max(MinTree,Ans[i]));
    printf("%lld\n",res);

    return 0;
}

T3 C.蔡老板学数学(DP NTT)

题目链接
???

考试代码

T2

/*
对于每个点i,求其MST上的最大边。其MST上i的度数只能为1。
对于每个连通块单独求其MST,当根节点不在其中时显然它就可以是答案。然而这没啥用,很容易有个大连通块。应该是二档部分分?
根节点只能连出一条,它的边只有把它与连通块连起来,而不能再帮助其它点并入连通块、so直接选其最小边即可。那么去掉根节点做个MST。
即求:i=1~n,去掉点i的最小生成树的最大边。只求和,算贡献?
LCT!不对啊,cut掉i的边 还是要枚举m条连起来。。而且忘了怎么写了。我还是咸鱼吧。
瞎写的 没调完 也没啥意思。
*/
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define gc() getchar()
//#define MAXIN 400000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define Vec std::vector<int>
typedef long long LL;
const int N=1e6+5,S=4*N,INF=0x3f3f3f3f;

int n,m,Enum,H[N],nxt[N<<1],to[N<<1],len[N<<1],tmp[N<<1],mn[N],Index,low[N],dfn[N],top,sk[N],fa[N],scnt,val[N],ff[N],Ans[N];
Vec scc[N],edge[N],bel[N];
//char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
    int fr,to,val;
    Edge() {}
    Edge(int f,int t,int v):fr(f),to(t),val(v) {}
    bool operator <(const Edge &a)const{
        return val<a.val;
    }
}e[N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
inline void AddEdge(int w,int u,int v)
{
    mn[v]=std::min(mn[v],w), to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
    mn[u]=std::min(mn[u],w), to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, len[Enum]=w;
    e[Enum>>1]=Edge(u,v,w);
}
namespace NoSolution
{
    bool ok[N];
    void DDFS(int x)
    {
        ok[x]=1;
        for(int i=H[x]; i; i=nxt[i])
            if(!ok[to[i]]) DDFS(to[i]);
    }
    bool Check()
    {
        DDFS(1);
        for(int i=2; i<=n; ++i) if(!ok[i]) return 1;
        return 0;
    }
}
namespace Violence
{
    int fa[N];
    int Get_fa(int x){
        return x==fa[x]?x:fa[x]=Get_fa(fa[x]);
    }
    int Kruskal(int id)
    {
        for(int i=1; i<=n; ++i) fa[i]=i;
        int res=0;
        for(int r1,r2,k=2,i=1; i<=m; ++i)
        {
            if(e[i].fr==id||e[i].to==id) continue;
            if((r1=Get_fa(e[i].fr))==(r2=Get_fa(e[i].to))) continue;
            fa[r1]=r2, res=std::max(res,e[i].val);
            if(++k==n) return std::max(mn[id],res);
        }
        return -1;
    }
    void Main()
    {
        LL res=0;
        for(int t,i=1; i<=n; ++i) res+=Kruskal(i);
        printf("%lld\n",res);
    }
}
int Get_fa(int x){
    return x==ff[x]?x:ff[x]=Get_fa(ff[x]);
}
bool cmp_e(int a,int b){
    return e[a].val<e[b].val;
}
inline bool Check(int x,int s)
{
    for(int i=0,lim=bel[x].size(); i<lim; ++i) if(bel[x][i]==s) return 1;
    return 0;
}
int Kruskal(int now,int id)//某连通块内MST 
{
    Vec &s=scc[now]; int tot=s.size(),res=0,m=0;
    for(int i=1; i<=n; ++i) ff[i]=i;//
    for(int i=0,x; i<tot; ++i)
    {
        ff[x=s[i]]=x;
        for(int j=0,lim=edge[x].size(); j<lim; ++j)
            tmp[++m]=edge[x][j];
    }
    std::sort(tmp+1,tmp+1+m,cmp_e);
    for(int k=1,j=1,r1,r2; j<=m; ++j)
    {
        int i=tmp[j];
        if(e[i].fr==id||e[i].to==id||!Check(e[i].fr,now)||!Check(e[i].to,now)) continue;
        if((r1=Get_fa(e[i].fr))==(r2=Get_fa(e[i].to))) continue;
        fa[r1]=r2, res=std::max(res,e[i].val);
        if(++k==tot) return res;
    }
    return -1;
}
void Tarjan(int x)
{
    low[x]=dfn[x]=++Index, sk[++top]=x;
    for(int v,i=H[x]; i; i=nxt[i])
        if(!dfn[v=to[i]])
        {
            fa[v]=x, Tarjan(v), low[x]=std::min(low[x],low[v]);
            if(dfn[x]==low[v])
            {
                scc[++scnt].push_back(x), bel[x].push_back(scnt);
                do{
                    scc[scnt].push_back(sk[top]), bel[sk[top--]].push_back(scnt);
                }while(sk[top+1]!=v);
                val[scnt]=Kruskal(scnt,0);
                printf("scnt:%d %d\n",scnt,val[scnt]);
            }
            else if(dfn[x]<low[v]) --top/*!*/;
        }
        else if(v!=fa[x]) low[x]=std::min(low[x],dfn[v]);
}
int Calc(int now)
{
    static int vis[N],Time=0;
    ++Time; int res=0;
    for(int i=0,tmp,lim=bel[now].size(),x; i<lim; ++i)
    {
        x=bel[now][i];
        if((tmp=Kruskal(x,now))==-1) return -1;
        res=std::max(res,tmp), vis[x]=Time;
    }
    for(int i=1; i<=scnt; ++i) if(vis[i]!=Time) res=std::max(res,val[i]);
    printf("Calc(%d):%d\n",now,res);
    return std::max(res,mn[now]);
}

int main()
{
//  freopen(".in","r",stdin);

    n=read(), m=read(); memset(mn,0x3f,sizeof mn);
    for(int i=1; i<=m; ++i) AddEdge(read(),read(),read());
    if(NoSolution::Check()) return printf("%d\n",-n),0;

    std::sort(e+1,e+1+m);
//  if(1ll*n*m<=1e8) return Violence::Main(),0;
    for(int i=1; i<=m; ++i) edge[e[i].fr].push_back(i), edge[e[i].to].push_back(i);
    Tarjan(1);
    for(int i=1; i<=scnt; ++i)
        for(int j=0; j<scc[i].size(); ++j) printf("scc:%d:%d\n",i,scc[i][j]);
    LL res=0;
    for(int i=1; i<=n; ++i) res+=Calc(i);
    printf("%lld\n",res);

    return 0;
}

T3

0pts:(迷)

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mod (998244353)
typedef long long LL;
const int N=1e6+5;

int n,K,sz,S[12],pos[12];

int Check(LL x)
{
    static int tm[7];
    for(int i=1; i<=sz; ++i) tm[i]=0;
    for(; x; x/=10) ++tm[pos[x%10]];
    for(int i=1; i<=sz; ++i) if(tm[i]%3) return 0;
    return 1;
}
int Solve(LL K)
{
    LL res=0,lim=1;
    while(n--) lim*=10ll;
    LL bef=lim/10, start=bef/K;
    while(start*K<bef) ++start;
    for(LL i=start; i*K<lim; ++i) res+=Check(i*K);
    return res%mod;
}

int main()
{
//  freopen(".in","r",stdin);

    char s[12];
    scanf("%d%d%s",&n,&K,s+1); sz=strlen(s+1);
    for(int i=1; i<=sz; ++i) pos[S[i]=s[i]-'0']=i;
    if(n>15) return puts("0"),0;
    printf("%d\n",Solve(K));

    return 0;
}

15pts:

//数组感觉爆炸 没敢交。。然而sb了完全可以
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mod (998244353)
typedef long long LL;
const int N=1e6+5;

int n,K,sz,S[12],pos[12];
int f[102][250][3][3][3][3][3][3];//2e8
bool vis[102][250][3][3][3][3][3][3];

int DFS(int p,int rem,int r1,int r2,int r3,int r4,int r5,int r6)
{
    if(!p) return !rem&&!r1&&!r2&&!r3&&!r4&&!r5&&!r6;
    if(vis[p][rem][r1][r2][r3][r4][r5][r6])
        return f[p][rem][r1][r2][r3][r4][r5][r6];

    long long res=0;
    for(int i=1; i<=sz; ++i)
        switch(i)
        {
            case 1: res+=DFS(p-1,(rem*10+S[1])%K,(r1+1)%3,r2,r3,r4,r5,r6); break;
            case 2: res+=DFS(p-1,(rem*10+S[2])%K,r1,(r2+1)%3,r3,r4,r5,r6); break;
            case 3: res+=DFS(p-1,(rem*10+S[3])%K,r1,r2,(r3+1)%3,r4,r5,r6); break;
            case 4: res+=DFS(p-1,(rem*10+S[4])%K,r1,r2,r3,(r4+1)%3,r5,r6); break;
            case 5: res+=DFS(p-1,(rem*10+S[5])%K,r1,r2,r3,r4,(r5+1)%3,r6); break;
            case 6: res+=DFS(p-1,(rem*10+S[6])%K,r1,r2,r3,r4,r5,(r6+1)%3); break;
        }
    for(int i=0; i<=9; ++i)
        if(!pos[i]) res+=DFS(p-1,(rem*10+i)%K,r1,r2,r3,r4,r5,r6);

//  printf("DFS(%d,%d,%d,%d,%d,%d,%d,%d):%I64d\n",p,rem,r1,r2,r3,r4,r5,r6,res%mod);
    vis[p][rem][r1][r2][r3][r4][r5][r6]=1;
    return f[p][rem][r1][r2][r3][r4][r5][r6]=res%mod;
}

int main()
{
    char s[12];
    scanf("%d%d%s",&n,&K,s+1); sz=strlen(s+1);
    for(int i=1; i<=sz; ++i) pos[S[i]=s[i]-'0']=i;
    printf("%d\n",DFS(n,0,0,0,0,0,0,0));

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/SovietPower/p/9398455.html