T1

题意

将给定的正整数 \(n\),表示为只包含数字 \(1\)

分析

看起来是一道构造题,令 \(a_i\) 表示由 \(i\) 个 \(1\) 组成的正整数,注意到每个 \(a_i\) 的使用次数都可以通过增加 \(a_{i+1}\) 的使用次数来减少,暴力 \(dfs\) 是否利用 \(a_{i+1}\) 优化即可,考场上想出正解,期望得分 \(100pt\),实际得分 \(100pt\)。

Code

#include<iostream>
#include<vector>
#include<algorithm>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
long long n,ans=0x3f3f3f3f3f3f3f3f;
vector<long long>num={0,1,11,111,1111,11111,111111,1111111,11111111,111111111,1111111111,11111111111,111111111111,1111111111111,11111111111111,111111111111111,1111111111111111};
void Dfs(int dep,long long t,long long sum)
{
    if(!dep){ans=min(ans,(t?0x3f3f3f3f3f3f3f3f:sum));return;}
    int tmp=lower_bound(num.begin(),num.end(),t)-num.begin();
    Dfs(dep-1,(num[tmp]-t)%num[dep],sum+(num[tmp]-t)/num[dep]*dep+tmp),Dfs(dep-1,t%num[dep],sum+t/num[dep]*dep);
}
int main()
{
    freopen("one.in","r",stdin);
    freopen("one.out","w",stdout);
    IOS,cin>>n,Dfs(16,n,0),cout<<ans<<'\n';
    return 0;
}

T2

题意

给定一棵 \(n\) 个节点的树,\(m\)

分析

删边不好写,考虑并查集经典应用,把操作离线下来,删边改成加边,而询问操作其实是在问 \(x\)

那么问题转向如何在合并操作时维护集合的直径,注意到合并后新集合的直径端点必然也是原本两集合中的直径端点,故只需要对这四个点两两求路径长度即可。求路径长度必然要使用 \(LCA\),这里可以直接使用原树上的 \(LCA\),原因是树上路径唯一,只要 \(x,y\) 两点连通,那二者在任意时刻的 \(LCA\) 一定是原树上的 \(LCA\)。考场上写的 \(O(1)\) 修改 \(dfs\) 暴力查询加上链的特判,期望得分 \(40pt\),实际得分 \(80pt\),猜测可能是因为删边操作让每次 \(dfs\) 需要遍历的连通块大小变小,跑不满 \(O(n)\),当然可能也有数据过水的原因。

Code

#include<iostream>
#include<utility>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N=5e5+5;
const int LogN=20;
int n,m,head[N],nxt[N<<1],to[N<<1],cnt=0,dep[N],father[N][LogN],leader[N],ans[N];
bool vis[N];
pair<int,int>Edge[N];
struct Node
{
    int pa,pb,dia;
}Tree[N];
struct Data
{
    int opt,x;
}Ques[N];
void Add(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void Dfs(int u,int fa)
{
    dep[u]=dep[fa]+1,father[u][0]=fa;
    for(int i=1;i<LogN;i++)
        father[u][i]=father[father[u][i-1]][i-1];
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa) continue;
        Dfs(v,u);
    }
}
int LCA(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=LogN-1;i>=0;i--)
        if(dep[father[u][i]]>=dep[v])
            u=father[u][i];
    if(u==v) return u;
    for(int i=LogN-1;i>=0;i--)
        if(father[u][i]!=father[v][i])
            u=father[u][i],v=father[v][i];
    return father[u][0];
}
int Dis(int u,int v)
{
    return dep[u]+dep[v]-2*dep[LCA(u,v)];
}
int Find(int k)
{
    if(leader[k]!=k) return leader[k]=Find(leader[k]);
    return k;
}
void Merge(int u,int v)
{
    int fu=Find(u),fv=Find(v);
    leader[fu]=fv;
    Node res=(Tree[fu].dia>Tree[fv].dia?Tree[fu]:Tree[fv]);
    int maxx=Dis(Tree[fu].pa,Tree[fv].pa);
    if(res.dia<=maxx)
        res=(Node){Tree[fu].pa,Tree[fv].pa,maxx};
    maxx=Dis(Tree[fu].pa,Tree[fv].pb);
    if(res.dia<=maxx)
        res=(Node){Tree[fu].pa,Tree[fv].pb,maxx};
    maxx=Dis(Tree[fu].pb,Tree[fv].pa);
    if(res.dia<=maxx)
        res=(Node){Tree[fu].pb,Tree[fv].pa,maxx};
    maxx=Dis(Tree[fu].pb,Tree[fv].pb);
    if(res.dia<=maxx)
        res=(Node){Tree[fu].pb,Tree[fv].pb,maxx};
    Tree[fv]=res;
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    IOS;
    cin>>n>>m;
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        Add(u,v),Add(v,u);
        Edge[i]={u,v};
    }
    for(int i=1;i<=m;i++)
    {
        cin>>Ques[i].opt>>Ques[i].x;
        if(Ques[i].opt==1) vis[Ques[i].x]=true;
    }
    Dfs(1,0);
    for(int i=1;i<=n;i++)
        leader[i]=i,Tree[i]={i,i,0};
    for(int i=1;i<n;i++)
        if(!vis[i]) Merge(Edge[i].first,Edge[i].second);
    for(int i=m;i>=1;i--)
    {
        if(Ques[i].opt==1) Merge(Edge[Ques[i].x].first,Edge[Ques[i].x].second);
        else ans[i]=max(Dis(Ques[i].x,Tree[Find(Ques[i].x)].pa),Dis(Ques[i].x,Tree[Find(Ques[i].x)].pb));
    }
    for(int i=1;i<=m;i++)
        if(Ques[i].opt==2) 
            cout<<ans[i]<<'\n';
    return 0;
}

T3

题意

给定正整数 \(n\),构造 \(n\) 个节点的无向连通图,令第 \(i\) 个点到点 \(1\) 的距离为 \(d_i\),满足 \(\forall i \in [1,n-1],d_i < d_n\),求满足条件的图的数量。

分析

考虑把 \(n\) 个点分布在 \(k\) 层上,第 \(i\) 层与点 \(1\) 的距离是 \(i\),第 \(0\) 层只有点 \(1\),第 \(k\) 层只有点 \(n\),这样就保证了 \(d_n\)

那么剩下的就是一个组合问题,把 \(n-2\) 个点分布至 \(k-1\) 层,保证每层不为空,且层与层之间至少有一条连边的连边方案数。由于层内连边不影响最短路径的长度,所以在这一点上不加以限制。设置状态 \(f_{s,x}\) 表示已经放了 \(s\) 个点,最后一层点数为 \(x\) 的方案数,那么考虑由 \(f_{s,x}\) 到 \(f_{s+y,y}\) 的转移,从剩下的点里选出 \(y\) 个点的方案数是 \(C_{n-2-s}^y\) 种,层内连边 \(2^{C_y^2}\) 种方案,两层之间共可以连 \(xy\) 条边,层间连边方案数 \(2^{xy}-1\) (保证至少连一条边),使用乘法原理计算。那么答案就是 \(\Sigma_{i=1}^{n-2} f_{n-2}^i \times (2^{n-2}-1)\)。考场上想到正解,期望得分 \(100pt\) 实际得分 \(100pt\)。

Code

#include<iostream>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0),cout.tie(0)
using namespace std;
const int MOD=1e9+7;
const int N=1005;
int n;
long long fac[N],inv[N],pow2[N],pow2C[N],a[N][N],f[N][N],ans=0;
long long quick_pow(long long a,long long b)
{
    long long res=1,base=a;
    while(b)
    {
        if(b&1) res=res*base%MOD;
        base=base*base%MOD;
        b>>=1;
    }
    return res;
}
void init(int maxx)
{
    fac[0]=1;
    for(int i=1;i<=maxx;i++)
        fac[i]=fac[i-1]*i%MOD;
    inv[maxx]=quick_pow(fac[maxx],MOD-2);
    for(int i=maxx-1;i>=0;i--)
        inv[i]=inv[i+1]*(i+1)%MOD;
    pow2[0]=1;
    for(int i=1;i<=maxx;i++)
        pow2[i]=pow2[i-1]*2%MOD;
    for(int i=0;i<=maxx;i++)
        pow2C[i]=quick_pow(2,i*(i-1)/2);
    for(int i=1;i<=maxx;i++)
    {
        long long base=(pow2[i]-1+MOD)%MOD;
        a[i][1]=base;
        for(int j=2;j<=maxx;j++)
            a[i][j]=a[i][j-1]*base%MOD;
    }
}
int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    IOS;
    cin>>n;
    init(n-2);
    f[0][1]=1;
    for(int s=1;s<=n-2;s++)
    {
        for(int x=1;x<=s;x++)
        {
            long long tmp=0;
            for(int y=1;y<=n-2;y++)
                if(s-x>=0) tmp=(tmp+a[x][y]*f[s-x][y])%MOD;
            f[s][x]=pow2C[x]*inv[x]%MOD*tmp%MOD;
        }
    }
    for(int i=1;i<=n-2;i++)
        ans=(ans+f[n-2][i])%MOD;
    cout<<ans*fac[n-2]%MOD<<'\n';
    return 0;
}

T4

题意

定义序列大小的比较方式,对在 \(A,B\) 两个序列中出现的元素从小到大考虑,对于元素 \(x\),若其在 \(A\) 中出现的次数大于在 \(B\) 中出现的次数,则序列 \(A\) 更大,反之亦然。对于给定的长度为 \(n\) 的序列,求其排名为 \(k\)

分析

据说是道黑题,考场上写了个暴力排序,期望得分 \(40pt\),实际得分 \(40pt\),小数据范围给的分意外的多,看题解写的是随机二分,目前还没订正。