清北考前刷题day3下午好

/*
可以并查集维护
可以发现,某个联通快出现大于等于2个环,一定无法分配。
有解要么一个环,要么没有环。
一个环时答案等于点数乘2(顺时针或逆时针)。
没有环是树,对于一个n个点的树,方案一定有n种(不连某个点)。
*/
#include<iostream>
#include<cstdio>
#include<cstring>

#define N 100007
#define mod 1000000007
#define ll long long

using namespace std;
ll n,m,ans,cnt;
ll fa[N],siz[N],num[N];
bool vis[N];

inline ll read()
{
    ll x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

ll find(ll x){return x==fa[x]?x:fa[x]=find(fa[x]);}

void merge(ll x,ll y)
{
    fa[y]=x;
    siz[x]+=siz[y];num[x]+=num[y];
}

int main()
{
    ll x,y;ans=1;
    n=read();m=read();
    for(ll i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(ll i=1;i<=m;i++)
    {
        x=read();y=read();
        ll r1=find(x),r2=find(y);
        if(r1!=r2) merge(r1,r2);
        else num[r1]++;
    } 
    for(ll i=1;i<=n;i++)
    {
        ll now=find(i);
        if(vis[now]) continue;vis[now]=1;
        if(num[now]>2) continue;
        if(num[now]==1) ans=(ans*2)%mod;
        if(!num[now]) ans=(ans*siz[now])%mod;
    }
    printf("%lld
",ans%mod);
    return 0;
} 

/*
若两数k进制下相同
那么k进制后它们一定偶数位为0,奇数位为0~k-1
从高位递推可能的情况累加答案即可。 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>

#define N 100
#define ll long long

using namespace std;
ll n,k,ans,cnt,len;
ll Pow[N];
int a[N];

int main()
{
    scanf("%lld%lld",&n,&k);
    while(n){Pow[++len]=n%k;n/=k;}
    if(len%2==0)
    {
        ans=pow(k,len>>1);
        printf("%lld
",ans);
    }
    else
    {
        for(int i=len;i>=1;i--)
        {
            if(Pow[i])
            {
                if(i%2==0)
                {
                    ans+=1ll*pow(k,i/2);
                    break;
                }
                else ans+=1ll*Pow[i]*pow(k,i/2);
                if(i==1) ++ans;
            }
        }
    }
    printf("%lld
",ans);
    return 0;
}

旅行

/*
嗯,需要维护每个点到根的距离。
首先开始的时候选取叶子结点一定比中间节点优。 
当选择了一条链的时候,会对哪些点有影响呢?
答案当然是在这条链上的点的子树。把这个点的子树权值减掉这个点的权值就好。
看到子树,想到dfs序。又因为要查询最大值,所以可以想到用线段树实现。
线段树每个节点维护原树每个点到根的距离的最大值和原树每个节点dfs序所对应的点的编号。 
每次查询区间最大值,然后删去这条链,每次删的时候更新子树权值(区间减法)。
删除把这个点的权值赋值为0就好。然后往上走,走的时候到某个点权值为0那么就停。
因为如果某个点权值为0,那么他到根的路径上所有点权都为零。恩。 
*/
#include<iostream>
#include<cstdio>
#include<cstring>

#define N 200007
#define ll long long

using namespace std;
ll n,m,ans,cnt,tot;
ll head[N],dis[N],fa[N];
ll S[N],pos[N],T[N],a[N];
struct edge{
    int u,v,net,w;
}e[N<<1];
struct tree{
    ll l,r,mx,pos,flag;
}tr[N<<2];

namespace seg
{
    void pushup(int k)
    {
        if(tr[k<<1].mx>tr[k<<1|1].mx) tr[k].mx=tr[k<<1].mx,tr[k].pos=tr[k<<1].pos;
        else tr[k].mx=tr[k<<1|1].mx,tr[k].pos=tr[k<<1|1].pos;
    }
    void pushdown(int k)
    {
        tr[k<<1].flag+=tr[k].flag;tr[k<<1|1].flag+=tr[k].flag;
        tr[k<<1].mx+=tr[k].flag;tr[k<<1|1].mx+=tr[k].flag;
        tr[k].flag=0;
    }
    void build(int k,int l,int r)
    {
        tr[k].l=l;tr[k].r=r;
        if(l==r) 
        {
            tr[k].mx=dis[pos[l]],tr[k].pos=pos[l];
            return;
        }
        int mid=l+r>>1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        pushup(k);
    }
    void update(int k,int l,int r,int z)
    {
        if(tr[k].l==l && tr[k].r==r) 
        {
            tr[k].mx+=z;tr[k].flag+=z;
            return;
        }
        pushdown(k);
        int mid=tr[k].l+tr[k].r>>1;
        if(r<=mid) update(k<<1,l,r,z);
        else if(l>mid) update(k<<1|1,l,r,z);
        else update(k<<1,l,mid,z),update(k<<1|1,mid+1,r,z);
        pushup(k);
    }

}using namespace seg;

inline void add(int u,int v)
{
    e[++cnt].v=v;e[cnt].net=head[u];head[u]=cnt;
}

inline ll read()
{
    ll x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

void dfs(int u,int last,ll sum)
{
    S[u]=++tot;pos[tot]=u;dis[u]=sum;
    for(int i=head[u];i;i=e[i].net)
    {
        int v=e[i].v;
        if(v==last) continue;
        fa[v]=u;dfs(v,u,sum+a[v]);
    }T[u]=tot;
}

void change(int u)
{
    while(a[u])
    {
        update(1,S[u],T[u],-a[u]);
        a[u]=0;u=fa[u];
    }
}

int main()
{
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    int x,y;
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<n;i++) 
    {
        x=read();y=read();
        add(x,y);add(y,x);
    }ans=a[1];a[1]=0;dfs(1,0,0);
    build(1,1,n);
    while(m--)
    {
        tree Tr=tr[1];ans+=Tr.mx;
        change(Tr.pos);
    }
    printf("%lld
",ans);
    return 0;
}
折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
原文地址:https://www.cnblogs.com/L-Memory/p/7757980.html