HGOI20190809 省常中互测2

  Problem A 时之终结

构造一个含有$n$个节点的无重边无自环的有向图,

使得从$1$出发,每一次经过一条$(u,v) (u < v)$的边到达节点$n$的方案恰好有$y$种。

对于$100\%$的数据,输出的无向图顶点树$n \leq 64 $给出的$y \leq 10^{18}$

Sol : 首先构造$63$个点的完全图,然后向第64个顶点连边,原问题等价于将$y$二进制拆分。

   这样构造可以获得满分:复杂度$O( {log_2 n}^ 2)$

# include <bits/stdc++.h>
# define int long long
using namespace std;
bool mp[65][65];
signed main()
{
    int y; scanf("%lld",&y); 
    int cnt=0;
    for (int i=1;i<=63;i++) for (int j=i+1;j<=63;j++) mp[i][j]=1,cnt++;
    for (int i=0;i<=63;i++) if ((y>>i)&1ll) mp[i+2][64]=1,cnt++;
    printf("64 %lld\n",cnt);
    for (int i=1;i<=64;i++)
     for (int j=i+1;j<=64;j++)
      if (mp[i][j]) printf("%lld %lld \n",i,j);
    return 0;
}
A.cpp

  Problem B 博士之时

$n$个节点,每个节点的度最多为$2$的图,共有$2$种不同的颜色,每一条相邻边都是不同色(0/1)的。

求出有多少种可能的重排置换,使得每个一对原本没有通过某种颜色进行连接的节点,出现在了这种颜色的连接中。

对于$100\%$的数据$n\leq 2\times 10^5$ , 图中每个点的度数最多为$2$。

Sol : 图中所有节点的度数小于等于2的可能只有是环或者链,而整个图一定是由一些单独的环和单独的链和孤立的点组成的。

     考虑链的情况,相同长度的链是可以互相交换的,同时考虑含有奇数条边的链又可以中心对称翻转的,而含偶数条边的链不行。

     考虑环的情况,相同长度的环是可以相互交换的,同时考虑每个环是可以转动这个环中的节点数次的。

     所以,我们有下列算法。

   对于链,分含奇数条边的链和偶数条边的链的讨论。

  • 奇数条边的链(设长度为$i$的奇数条边有$j$条): $2^j \times j!$
  • 偶数条边的链(设长度为$i$的奇数条边有$j$条):$ j!$

   对于环,无需分奇数偶数讨论。

     设长度为$i$的环有$j$个:$j! \times i^j$ 

       设有$k$个孤立点,显然这些孤立点可以任意排列:$k!$

    将上述情况相乘就是合法的答案,如果用$n!$减去就是不合法的答案。

  复杂度是$O(n)$ 

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10,mo=1e9+7;
struct rec{  int pre,to,w; rec() { w=-1; } }a[N<<1];
bool vis[N];
int du[N],head[N],jc[N],Pow2[N];
int tot,n,m1,m2,cnt;
bool flag;
int col;
map<int,int>circle,chain_even;
map<pair<int,int>,int>chain_odd;
int Pow(int x,int n)
{
    int ans=1;
    while (n) {
        if (n&1) ans=ans*x%mo;
        x=x*x%mo;
        n>>=1;
    }
    return ans%mo;
}
void adde(int u,int v,int w)
{
    a[++tot].pre=head[u];
    a[tot].to=v;
    a[tot].w=w;
    head[u]=tot;
}
void dfs1(int u)
{
    vis[u]=1;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (vis[v]) continue;
        if (!flag) col=a[i].w,flag=1; 
        cnt++; dfs1(v);
    }
}
void dfs2(int u)
{
    vis[u]=1;
    for (int i=head[u];i;i=a[i].pre) {
        int v=a[i].to; if (vis[v]) continue;
        cnt++; dfs2(v);
    }
}
signed main()
{
    int num; scanf("%lld",&num);
    scanf("%lld%lld%lld",&n,&m1,&m2);
    for (int i=1;i<=m1;i++) {
        int u,v; scanf("%lld%lld",&u,&v);
        adde(u,v,0); adde(v,u,0);
        du[u]++; du[v]++;
    }
    for (int i=1;i<=m2;i++) {
        int u,v; scanf("%lld%lld",&u,&v);
        adde(u,v,1); adde(v,u,1);
        du[u]++; du[v]++;
    }
    
    jc[0]=1; for (int i=1;i<=200000;i++) jc[i]=jc[i-1]*i%mo;
    Pow2[0]=1; for (int i=1;i<=200000;i++) Pow2[i]=Pow2[i-1]*2%mo; 
    
    for (int i=1;i<=n;i++) if (!vis[i] && du[i]==1) {
        cnt=0; col=-1; flag=false; dfs1(i);  
        if (cnt&1) chain_odd[make_pair(cnt,col)]++;
        else chain_even[cnt]++;
    }
    for (int i=1;i<=n;i++) if (!vis[i] && du[i]==2) {
        cnt=1; dfs2(i);  circle[cnt]++;
    }
    int res=0;
    for (int i=1;i<=n;i++) if (!vis[i]) res++;
    
    int ret=jc[res]; map<int,int>::iterator it;
    for (it = circle.begin() ; it != circle.end() ; ++it) {
        int i=it->first,j=it->second;
        ret=ret*jc[j]%mo*Pow(i,j)%mo; 
    } 
    for (it = chain_even.begin(); it!=chain_even.end() ; ++it) {
        int i=it->first,j=it->second;
        ret=ret*jc[j]%mo;
    }
    map<pair<int,int>,int>::iterator it2;
    
    for (it2 = chain_odd.begin() ; it2 != chain_odd.end() ; ++it2) {
        pair<int,int> p=it2->first; int i=p.first,j=it2->second; 
        ret=ret*jc[j]%mo*Pow2[j]%mo;
    }
    
    int ans = ((jc[n] - ret) % mo + mo) % mo;
    printf("%lld\n",ans); 
    
    return 0;
}
B.cpp

  Problem C 曾有两次

 给出无向连通图$G$,有$n$个点和$m$条边,

 对于每一个点,求删除一条边后这个点到$1$节点的距离可能的最大值

 对于$100\%$的数据满足,$n\leq 2\times 10^5 , m \leq 5 \times 10^5$

 Sol: 删除的边一定是最短路树上该点和其父节点的连边。

  于是直接建出最短路树,然后依次考虑每一条非树边$(u,v)$

  对于链$u -> lca(u,v) $ 不含lca的所有点都可以先最短路树的叶子节点走,经过枚举的这一条边,然后从$v$节点走到根。

  对于链$v -> lca(u,v) $ 不含lca的所有点同理。

  这样对于在链上的一个节点$k$,那么对其用$dist(k,u) + w(u,v) + dist(1,v)$更新即可。

     注意到$dist(k,u)$ 这个可以表示为$dist(1,u) - dist(1,k)$ 原来的式子可以化为 $dist(1,u) - dist(1,k) + w(u,v) + dist(1,v) $

  只有$dist(1,k)$是定值,所以我们可以最后统一处理,我们只需要使用$dist(1,u)  + w(u,v) + dist(1,v) $来更新每个节点的答案即可。

  这就变成了在树上维护区间更新一个min值然后单点查询,直接使用树链剖分维护即可。

  复杂度是$O(m { log_2 }^2n )$

# pragma GCC optimize(3) 
# include <bits/stdc++.h>
# define int long long 
# define inf (1e14)
using namespace std;
const int N=2e5+10;
const int M=5e5+10;
struct rec{ int pre,to,w;}a[M<<1];
struct edge{ int u,v,w;}rec[M<<1];
int n,m,tot=1;
int head[N],d[N],e[N],f[N],dep[N],size[N];
int dfn[N],top[N],g[N][22];
bool b[M<<1];
vector<int>E[N];
pair<int,int>pre[N];
inline int read()
{
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void write(int x)
{
    if (x>9) write(x/10);
    putchar('0'+x%10);
}
void adde(int u,int v,int w)
{
    a[++tot].pre=head[u];
    a[tot].to=v; 
    a[tot].w=w;
    head[u]=tot;
}
struct node {
    int id,len;
};
struct cmp {
    bool operator () (node x,node y) {
        return x.len > y.len ;
    }
};
priority_queue<node,vector<node>,cmp>q;
bool vis[N];
void dijkstra(int s)
{
    memset(vis,false,sizeof(vis));
    memset(d,0x3f,sizeof(d));
    d[s]=0; q.push((node){s,0});
    while (!q.empty()) {
        node u=q.top();q.pop();
        if (vis[u.id]) continue;
        vis[u.id]=1;
        for (int i=head[u.id];i;i=a[i].pre) {
            int v=a[i].to,w=a[i].w;
            if (d[v]-w>d[u.id]) {
                e[v]=i; pre[v]=make_pair(u.id,w); d[v]=d[u.id]+w; 
                q.push((node){v,d[v]});
            }
        }
    }
}
void dfs1(int u,int fa)
{
    f[u]=fa; dep[u]=dep[fa]+1; size[u]=1;
    for (int i=0;i<E[u].size();i++) {
        int v=E[u][i]; if (v==fa) continue;
        dfs1(v,u); size[u]+=size[v];
    }
}
void dfs2(int u,int fa,int tp)
{
    dfn[u]=++dfn[0]; top[u]=tp; 
    int son=-1;
    for (int i=0;i<E[u].size();i++) {
        int v=E[u][i]; if (v==fa) continue;
        if (son==-1||size[v]>size[son]) son=v;
    }
    if (son!=-1) dfs2(son,u,tp);
    for (int i=0;i<E[u].size();i++) {
        int v=E[u][i]; if (v==fa) continue;
        if (v!=son) dfs2(v,u,v);
    }
}
# define ls (x<<1)
# define rs (x<<1|1)
# define lson ls,l,mid
# define rson rs,mid+1,r
# define mid (l+r>>1)
struct Segmengt_Tree{
    int tag,ret;
    Segmengt_Tree() { tag=-1; ret=inf;}
}tr[N<<2];
void down(int x)
{
    if (tr[x].tag==-1) return;
    tr[ls].ret=min(tr[ls].ret,tr[x].tag);
    tr[rs].ret=min(tr[rs].ret,tr[x].tag);
    tr[ls].tag=(tr[ls].tag==-1)?tr[x].tag:min(tr[x].tag,tr[ls].tag);
    tr[rs].tag=(tr[rs].tag==-1)?tr[x].tag:min(tr[x].tag,tr[rs].tag);
    tr[x].tag=-1; 
}
void update(int x,int l,int r,int opl,int opr,int d)
{
    if (opl<=l &&r<=opr) {
        if (tr[x].tag==-1) tr[x].tag=d;
        else tr[x].tag=min(tr[x].tag,d);
        tr[x].ret=min(tr[x].ret,d);
        return;
    }
    down(x);
    if (opl<=mid) update(lson,opl,opr,d);
    if (opr>mid) update(rson,opl,opr,d);
    tr[x].ret=min(tr[ls].ret,tr[rs].ret); 
}
int query(int x,int l,int r,int opl,int opr)
{
    if (opl<=l&&r<=opr) return tr[x].ret;   
    down(x);
    int ret=inf;
    if (opl<=mid) ret=min(ret,query(lson,opl,opr));
    if (opr>mid) ret=min(ret,query(rson,opl,opr));
    return ret;
}
int lca(int u,int v)
{
    if (dep[u]<dep[v]) swap(u,v);
    for (int i=21;i>=0;i--)
     if (dep[g[u][i]]>=dep[v]) u=g[u][i];
    if (u==v) return u;
    for (int i=21;i>=0;i--)
     if (g[u][i]!=g[v][i]) u=g[u][i],v=g[v][i];
    return g[u][0];  
}
void update(int u,int v,int d)
{
    int f1=top[u],f2=top[v];
    while (f1!=f2) {
        if (dep[f1]<dep[f2]) swap(f1,f2);
        update(1,1,n,dfn[f1],dfn[u],d);
        u=f[f1]; f1=top[u];
    }
    if (dep[u]<dep[v]) swap(u,v);
    update(1,1,n,dfn[v]+1,dfn[u],d);
}
int query(int x){
    return query(1,1,n,dfn[x],dfn[x]);
}
main()
{
    int num=read();
    n=read();m=read();
    for (int i=1;i<=m;i++) {
        int u=read(),v=read(),w=read();
        adde(u,v,w); rec[tot]=(edge){u,v,w}; adde(v,u,w);
    }
    dijkstra(1);
    for (int i=2;i<=n;i++) {
        b[e[i]]=b[e[i]^1]=1; 
        E[i].push_back(pre[i].first);
        E[pre[i].first].push_back(i);
    }
    dfs1(1,0); dfs2(1,0,0);
    for (int i=1;i<=n;i++) g[i][0]=f[i];
    for (int i=1;i<=21;i++) for (int j=1;j<=n;j++) g[j][i]=g[g[j][i-1]][i-1];
    for (int i=2;i<=tot;i+=2)
     if (!b[i]) {
        int u=rec[i].u,v=rec[i].v,w=rec[i].w,l=lca(u,v);
        update(u,l,d[v]+w+d[u]); update(v,l,d[u]+w+d[v]);
     }
    putchar('0'); putchar(' ');
    for (int i=2;i<=n;i++) { 
        int t=query(i);
        if (t>=inf) putchar('-'),putchar('1');
        else write(t-d[i]);
        putchar(' ');
    } 
    putchar('\n');
    return 0;
}
C.cpp

猜你喜欢

转载自www.cnblogs.com/ljc20020730/p/11328170.html
今日推荐