JSOI2015 总结

[JSOI2015]salesman

题目链接

树形 \(dp\)。一个点可以走 \(x\) 次,相当于最多可以进入 \(x-1\) 棵子树,每一次挑收益最大且大于 \(0\) 的走就可以取到最大值。至于判断多解,只需要看有没有两颗子树的收益相同或者有收益为 \(0\),即可走也不走的子树。如果从子树走有多解,那么从根走也一定有多解,额外开一个数组记录是否存在多解,\(dp\) 时直接维护。

#include <cstdio>
#include <algorithm>
#define inf (2100000000)
typedef long long ll;
inline int rd(){
    int x=0,p=1;
    char a=getchar();
    while((a<48||a>57)&&a!='-')a=getchar();
    if(a=='-')p=-p,a=getchar();
    while(a>47&&a<58)x=(x<<1)+(x<<3)+(a&15),a=getchar();
    return x*p;
}
inline int min(int x,int y){return x<y?x:y;}
const int N=100002;
struct Edge{
    int to,next;
}edge[N<<1];
int head[N],cnt;
int n;
int a[N],b[N];
int f[N],vis[N],st[N];
bool comp(const int &x,const int &y){
    return f[x]>f[y];
}
inline void add(int f,int t){
    edge[++cnt].next=head[f];
    edge[cnt].to=t;
    head[f]=cnt;
}
inline void dp(int u,int ft){
    f[u]=a[u];
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==ft)continue;
        dp(v,u);
    }
    int hd=0,top=0;
    for(int i=head[u];i;i=edge[i].next){
        int v=edge[i].to;
        if(v==ft)continue;
        st[++top]=v;
    }
    std::sort(st+1,st+top+1,comp);
    while(hd<min(top,b[u]-1)&&f[st[hd+1]]>=0)
        f[u]+=f[st[++hd]],vis[u]|=vis[st[hd]];
    if(!hd)return;
    if((hd<top&&f[st[hd]]==f[st[hd+1]])||f[st[hd]]==0)vis[u]=1;
}
int main(){
    n=rd(),b[1]=inf;
    for(int i=2;i<=n;i++)a[i]=rd();
    for(int i=2;i<=n;i++)b[i]=rd();
    for(int i=1;i<n;i++){
        int u=rd(),v=rd();
        add(u,v),add(v,u);
    }
    dp(1,0);
    printf("%d\n",f[1]);
    if(vis[1])puts("solution is not unique");
    else puts("solution is unique");
    return 0;
}

[JSOI2015]子集选取

题目链接

结论题,答案为 \(2^{nk}\)

大致推导:由于集合内的元素不影响结果,可以先算出 \(n=1\) 时的结果 \(ans\),最后答案为 \(ans^n\)。此时只有一种元素,所以如果一个子集里没有这种元素,则一个包含这个元素的集合只可能存在于它的右上方。这相当于从三角形左下向上或向右走 \(k\) 步,这样走出来一条线,上方的子集包含这个元素,下方的没有包含。总方案数是 \(2^k\),有 \(n\) 种元素答案就是 \(2^{nk}\)

#include <cstdio>
typedef long long ll;
inline ll rd(){
    ll x=0,p=1;
    char a=getchar();
    while((a<48||a>57)&&a!='-')a=getchar();
    if(a=='-')p=-p,a=getchar();
    while(a>47&&a<58)x=(x<<1)+(x<<3)+(a&15),a=getchar();
    return x*p;
}
const ll mod=1e9+7;
ll n,k; 
inline ll fpow(ll b,ll p){
    ll ans=1,tmp=b;
    while(p){
        if(p&1)ans=ans*tmp%mod;
        tmp=tmp*tmp%mod;
        p>>=1;
    }
    return ans;
}
int main(){
    n=rd(),k=rd();
    printf("%lld\n",fpow(2,n*k));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wsk1202/p/12356526.html
今日推荐