2017 清北济南考前刷题Day 3 afternoon

期望得分:100+40+100=240

实际得分:100+40+100=240

 

将每个联通块的贡献乘起来就是答案

如果一个联通块的边数>点数 ,那么无解

如果边数=点数,那么贡献是 2

如果边数=点数-1,那么贡献是点数

#include<queue>
#include<cstdio>
#include<iostream>
 
using namespace std;

const int mod=1e9+7;

#define N 100001

int front[N],to[N<<1],nxt[N<<1],tot;

bool vis[N];

int d[N];

queue<int>q;

int ans=1;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
    d[v]++;
}

bool bfs(int s)
{
    while(!q.empty()) q.pop();
    int p=0,e=0;
    q.push(s); vis[s]=true;
    int now; 
    while(!q.empty())
    {
        now=q.front(); q.pop();
        p++; e+=d[now];
        for(int i=front[now];i;i=nxt[i])
            if(!vis[to[i]]) vis[to[i]]=true,q.push(to[i]);
    }
    if(e>p) return false;
    if(e==p) ans=ans*2%mod;
    else ans=1ll*ans*p%mod;
    return true;
}

int main()
{
    freopen("girl.in","r",stdin);
    freopen("girl.out","w",stdout);
    int n,m;
    read(n); read(m);
    int u,v;
    for(int i=1;i<=m;i++) 
    {
        read(u); read(v);
        add(u,v);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i]) 
            if(!bfs(i)) { cout<<0; return 0; }
    cout<<ans;
    return 0;
}
View Code

 

 

 

显然的结论:

若一个数的K进制和-k进制相同

那么他的k进制/-k进制的偶数位一定是0

然后乱搞就好了

也可以数位DP

 

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

typedef long long LL;

LL bit[67];

int a[67];

int main()
{    
    freopen("endless.in","r",stdin);
    freopen("endless.out","w",stdout);
    long long n;int k;LL ans=0;
    scanf("%I64d%d",&n,&k);
    int len=0;
    while(n) a[++len]=n%k,n/=k;
    if(!(len&1)) 
    {
        ans=pow(1LL*k,len/2);
        cout<<ans;
    }
    else 
    {
        for(int i=len;i>=1;i--)
            if(a[i])
            {
                if(!(i&1)) { ans+=pow(1LL*k,i/2); break; }
                ans+=1LL*a[i]*pow(1LL*k,i/2);
                if(i==1) ans++;
            }
        cout<<ans;
    }
}
View Code

 

40暴力 

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

int fbit[1001];

int a[1001],b[1001];

int pre[1001];

int main()
{
    freopen("endless.in","r",stdin);
    freopen("endless.out","w",stdout);
    int n,k;
    scanf("%d%d",&n,&k);
    fbit[0]=1;
    int la,lb; int x,ans=min(k-1,n)+1;
    for(int i=k;i<=n;i++)
    {
        la=0;
        x=i;
        while(x) a[la++]=x%k,x/=k; la--;
        x=i;
        int c=-1,now=0;
        pre[0]=k-1;
        while(1)
        {
            fbit[++now]=fbit[now-1]*c*k;
            if(!(now&1)) 
            {
                pre[now]=pre[now-2]+(k-1)*fbit[now];
                if(pre[now]>=x) break;
            }
            else pre[now]=pre[now-1];
        }
        lb=now;
        memset(b,0,sizeof(b));
        while(now)
        {
            if(!(now&1)) while(x>pre[now-2]) b[now]++,x-=fbit[now];
            else 
            {
                while(x<0 && abs(x)>pre[now-1]) b[now]++,x-=fbit[now];
                while(x>pre[now-1]) b[now]++,x+=fbit[now];
            }
            now--;
        }
        b[0]=x;
        if(la!=lb) continue;
        bool ok=true;
        for(int i=0;i<=la && ok ;i++)  if(a[i]!=b[i])  ok=false;
        if(!ok) continue;
    //    printf("%d\n",i);
        ans++;
    }
    cout<<ans;
}
View Code

 

 

 考场思路:

每次旅行一定是找当前贡献最大的叶子节点

用线段树维护所有的叶子节点的贡献

修改:

每个点只会修改一次

所以用并查集记录这个点到根节点路径上第一个没有被修改的点

修改沿着并查集的father找上去

对于每个要改的点,预处理出它会影响到的叶子节点,

按dfs到的叶子节点的顺序在线段树中加点,那每个点影响到的叶子节点就是一段连续的区间

dfs记下来,线段树区间修改即可

 

其实可以不用并查集

往上改到已经改过的点,直接break

#include<cstdio>
#include<iostream>
#include<algorithm>
 
using namespace std;

#define N 100001

typedef long long LL;

int n,m;
int val[N];

int front[N],nxt[N<<1],to[N<<1],tot;

int F[N],fa[N],id[N];

int cnt,L[N],R[N];

LL w[N];

LL mx[N<<2],pos[N<<2],tag[N<<2];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}

void init()
{
    read(n); read(m);
    for(int i=1;i<=n;i++) read(val[i]);
    int u,v;
    for(int i=1;i<n;i++)
    {
        read(u); read(v);
        add(u,v);
    }
} 

void dfs(int x,int f,LL sum)
{
    L[x]=cnt+1;
    F[x]=x; bool leaf=true;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=f) leaf=false,fa[to[i]]=x,dfs(to[i],x,sum+val[to[i]]);
    if(leaf)  w[++cnt]=sum,id[cnt]=x;  
    R[x]=cnt;
}

void build(int k,int l,int r)
{
    if(l==r) { mx[k]=w[l]; pos[k]=l; return; }
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
    pos[k]= mx[k]==mx[k<<1] ? pos[k<<1] : pos[k<<1|1];
}

void down(int k)
{
    mx[k<<1]-=tag[k];
    mx[k<<1|1]-=tag[k];
    tag[k<<1]+=tag[k];
    tag[k<<1|1]+=tag[k];
    tag[k]=0;
}

void change(int k,int l,int r,int opl,int opr,LL sum)
{
    if(l>=opl && r<=opr)
    {
        mx[k]-=sum;
        tag[k]+=sum;
        return;
    }
    if(tag[k]) down(k);
    int mid=l+r>>1;
    if(opl<=mid) change(k<<1,l,mid,opl,opr,sum);
    if(opr>mid) change(k<<1|1,mid+1,r,opl,opr,sum);
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
    pos[k]=mx[k]==mx[k<<1] ? pos[k<<1] : pos[k<<1|1];
}


int find(int i) { return F[i]==i ? i : F[i]=find(F[i]); }

void solve()
{
    int p; LL ans=0;
    while(m--)
    {
        p=id[pos[1]]; // 第pos个叶子节点 
        ans+=mx[1];
        while(p)
        {
            change(1,1,cnt,L[p],R[p],val[p]);
            F[p]=find(F[fa[p]]);
            p=F[p];
        }
    }
    cout<<ans;
}

int main()
{
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    init();
    dfs(1,0,val[1]);
    build(1,1,cnt);
    solve();
}
View Code

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/7755369.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值