Day3下

少女
【问题描述】
你是能看到第一题的 friends呢。
—— hja
少女在图上开车, 她们希望把每条边分配给与其相连的点中一个并且每个点最多被分配一条边,问可能的方案数。
【输入格式】
第一行两个整数 𝑁,𝑀代表点数和边。
接下来 𝑀行每两个整数代表一条边。
【输出格式】
一行个整数代表答案对 109+7取模之后的值 。
【样例输入】
5 4
1 2
3 2
4 5
4 5
【样例输出】
6
【数据范围与规定】
对于 20%的数据, 1≤𝑁≤10。
对于 40%的数据, 1≤𝑁≤100。
对于 100%的数据, 1≤𝑀≤𝑁≤105。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int P=1e9+7;
int h[N],nex[N*2],to[N*2],cnt;
int n,m;
void add(int x,int y)
{
    to[++cnt]=y,nex[cnt]=h[x],h[x]=cnt;
    to[++cnt]=x,nex[cnt]=h[y],h[y]=cnt;
    return ;
}
bool vis[N],have[N];
queue<int>q;
int dfs(int x)
{
    vis[x]=1;int ans=0,flag=1;
    for(int i=h[x];i;i=nex[i])
    if(!vis[to[i]])
    {        
        flag=0;
        if(!have[x])
        {
            have[x]=1;
            ans+=dfs(to[i]);
            have[x]=0;
        }
        have[to[i]]=1;
        ans+=dfs(to[i]);
        have[to[i]]=0;    
    }
    if(flag)    return 1;
    return ans;
}
int main()
{
    freopen("girl.in","r",stdin);
    freopen("girl.out","w",stdout);    
    scanf("%d%d",&n,&m);
    
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        
    }
    
    for(int i=1;i<=n;i++)
    if(!vis[i])
    {
        q.push(dfs(i));
    }
    long long ans=1;
    while(!q.empty())
    {
        ans=(ans*q.front())%P;
        q.pop();
    }
    cout<<ans<<'\n';
    return 0;
}
第一遍 希望有分 结果0 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int P=1e9+7;
int h[N],nex[N*2],to[N*2],cnt;
int n,m;
void add(int x,int y,int k)
{
    to[++cnt]=y,nex[cnt]=h[x],h[x]=cnt;    
    to[++cnt]=x,nex[cnt]=h[y],h[y]=cnt;
    return ;
}
bool vis[N],cirl;
long long ans=1,size;
void dfs(int x,int last)
{
    size++;    
    vis[x]=1;
    for(int i=h[x];i;i=nex[i])
    if(to[i]!=last)
    {        
        if(vis[to[i]])
        {
            cirl=1;
            continue;
        }else
        dfs(to[i],x);
    }
    return ;
}
int t1,t2;
int main()
{
    freopen("girl.in","r",stdin);
    freopen("girl.out","w",stdout);    
    scanf("%d%d",&n,&m);    
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y,i);        
    }    
    for(int i=1;i<=n;i++)
    if(!vis[i])
    {
        cirl=0;size=0;
        dfs(i,0);
        if(cirl)
            ans=(ans*2LL)%P,t1++;
        else
            ans=(ans*size)%P,t2++;
    }    
    cout<<ans<<'\n';
    return 0;
}
爆搜法AC

注意!默认自环有两种分配方案。所以我dfs(i,i)是错的!!!

 

并查集,维护联通块。

有环*2,无环(树)*sum。

~~~应该多找几个样例试试。 还差一点。 

或者dfs序找环。

 

终末
【问题描述】
你是能看到第二题的 friends呢。
—— laekov
没有尽头的世界之中,我们想知道 0−𝑁中有多少个数在 𝐾进制下和 −𝐾进制 下的表示方式一样 。(举个例子, 4的−3进制表示为 4=121−3=1×(−3)2+2×(−3)1+1×(−3)0)
【输入格式】
一行两个整数 𝑁,𝐾。
【输出格式】
一行个整数代表答案 。
【样例输入】
21 3
【样例输出】
9
【数据范围与规定】
对于 40%的数据 ,𝑁≤1000。
对于另外 30%的数据, 𝐾=2
对于 100%的数据, 1≤𝑁≤1015,2≤𝐾≤103。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
long long n,k;
long long  ans;
int sum;
inline void dfs(long long tot,long long  bas)
{
    if(tot>n)    return;
    ans++;
    for(int i=1;i<k;i++)    
        dfs(tot+i*bas,bas*k*k);
    return ;
}
int main()
{
    freopen("endless.in","r",stdin);
    freopen("endless.out","w",stdout);
    scanf("%lld%lld",&n,&k);
    k=abs(k);
    if(n<=1000)
    {    
        for(int i=0;i<k;i++)        
            dfs(i,k*k);
        cout<<ans<<'\n';
        return 0;
    }    
    sum=log2(n)/log2(k)+1;
    if(sum%2)
    {        
        ans=1;
        for(int i=3;i<=sum;i+=2)
            ans*=k;
        long long tot=0,bas=1;
        for(int i=0;i<=sum;i+=2)        
        {
            tot+=bas*(k-1);
            bas=bas*k*k;
        }
        if(tot<=n)
        {
            cout<<ans*k<<'\n';
            return 0;
        }
        bas/=k*k;
        tot=bas;
        for(int i=0;i<k;i++,tot+=bas)        
            dfs(tot+i,k*k);
        cout<<ans<<'\n';
        return 0;
    }else
    {
        sum=sum/2;
        ans=1;
        for(int i=1;i<=sum;i++)
            ans*=k;
        cout<<ans<<'\n';
    }    
    return 0;
}
希望有分60

 想到了做法,但实现不对。

奇数位为0,偶数为各种数。

dp ,!!

数位
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
LL n,k;
LL ans;
int sum;
int a[100];
int main()
{
    scanf("%lld%lld",&n,&k);
    int len=0;
    while(n)    a[++len]=n%k,n/=k;
    
    if(len&1)
    {
        for(int i=len;i>=1;i--)
        if(a[i])
        {
            if(!(i&1))
            {
                ans+=pow(k,i/2);
                break;
            }
            ans+=1LL*a[i]*pow(k,i/2);
            if(i==1) ans++;
        }
        cout<<ans;
    }else
        cout<<pow(k,len/2);
        
    return 0;
}
dp

 

 

旅行
【问题描述】
你是能看到第三题的 friends呢。
—— aoao
最后的旅行在树上,给你一棵 最后的旅行在树上,给你一棵 𝑁个点的树,每有权。次你可以选择 个点的树,每有权。次你可以选择 个点的树,每有权。次你可以选择 一个点开始旅行,获得这到 一个点开始旅行,获得这到 一个点开始旅行,获得这到 1号点 路径上所有的权,然后把这些号点 路径上所有的权,然后把这些号点 路径上所有的权,然后把这些号点 路径上所有的权,然后把这些权全部变成 0。你可以旅行 𝐾次,问能获得的最大点权和。

【输入格式】
第一行 两个整数 𝑁,𝐾。
接下来一行 𝑁个整数代表点权。
接下来 𝑁−1行每两个数代表树上一条边。
【输出格式】
输出一行代表答案 。
【样例输入】
5 2
4 3 2 1
1 2
1 5
2 3
2 4
【样例输出】
10
【数据规模与约定】
对于 30%的数据, 1≤𝑁≤10
对于 60%的数据, 1≤𝑁≤100。
对于 100%的数据, 1≤𝑁≤105,0≤𝐾≤𝑁,点权是不超过 ,点权是不超过 231−1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
const int N=1e2+10;
int h[N],nex[N*2],to[N*2],cnt;
int n,k;
int f[N],sum[N];
int d[N];
int low[N],tot;
void add(int x,int y)
{
    to[++cnt]=y,nex[cnt]=h[x],h[x]=cnt;
    to[++cnt]=x,nex[cnt]=h[y],h[y]=cnt;
    return ;
}
void build(int x,int fa)
{
    f[x]=fa;
    sum[x]+=sum[fa];
    bool flag=1;
    for(int i=h[x],v;i;i=nex[i])
    if(to[i]!=fa)
    {
        v=to[i];
        build(v,x);
        flag=0;
    }
    if(flag)    low[++tot]=x;
    return ;
}
void Build(int x)
{    
    sum[x]=d[x]+sum[f[x]];
    for(int i=h[x],v;i;i=nex[i])
    if(to[i]!=f[x])
    {
        v=to[i];
        Build(v);    
    }    
    return ;
}
int maxn=0,maxw;long long ans=0;
int main()
{
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&sum[i]);
        d[i]=sum[i];
    }
    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    build(1,0);    
    while(k--)
    {
        maxn=0;
        for(int i=1;i<=tot;i++)
        if(sum[low[i]]>maxn)
        {
            maxn=sum[low[i]];
            maxw=low[i];
        }
        ans+=maxn;
        sum[maxw]=0;
        while(maxw)    d[maxw]=0,maxw=f[maxw];        
        Build(1);
    }
    cout<<ans<<'\n';
    return 0;
}
希望有分60

 思路:

      我只写了60分就是见一棵树,求树上前缀和,每次都把整棵树都修改一边。复杂度上是kn。

      当然,这是可以优化的,我们需要更改的只是叶结点,

  树上前缀和,重算一遍。

  优化,区间修改,线段树来做。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cstring>
#ifdef WIN32
#define ll "%I64d"
#else
#define ll "%lld"
#endif
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
typedef long long LL;
const int M=100001;
int to[M<<1],nt[M<<1],h[M],a[M],cnt=1,tot;
LL w[M],c[M];
void insert_edge(int u,int v){
    to[cnt]=v;nt[cnt]=h[u];h[u]=cnt++;
    to[cnt]=u;nt[cnt]=h[v];h[v]=cnt++;
}
bool cmp(LL x,LL y){
    return x>y;
}
void dfs(int u,int fa){
    LL MAX=0;int vp;
    for(int i=h[u];i;i=nt[i]){
        int v=to[i];
        if(v!=fa){
            dfs(v,u);
            if(w[v]>MAX)MAX=w[v],vp=v;
        }
    }
    w[u]=MAX+a[u];
    for(int i=h[u];i;i=nt[i]){
        int v=to[i];
        if(v!=fa&&v!=vp)c[++tot]=w[v];
    }
}
int main(){
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    int n,k;
    scanf("%d%d",&n,&k);
    rep(i,1,n)scanf("%d",&a[i]);
    memset(h,sizeof(h),0);
    rep(i,1,n-1){
        int u,v;
        scanf("%d%d",&u,&v);
        insert_edge(u,v);
    }
    tot=0;
    dfs(1,1);
    c[++tot]=w[1];
    sort(c+1,c+tot+1,cmp);
    LL ans=0;
    k=min(k,tot);
    rep(i,1,k)ans+=c[i];
    printf(ll"\n",ans);
}
神代码啊

 

posted @ 2017-10-30 16:06  浪矢-CL  阅读(410)  评论(0编辑  收藏  举报