2016"百度之星" - 初赛(Astar Round2A)题解

今天是一年一度的百度之星初赛的日子啊,大家都早早的准备好了比(上)赛(分),运气也不错,涨了不少分,rating到了2222(真尼玛2)
ps.我是开黑做的,所以这分数不代表真实水平,勿黑

1001

给你一个由m个x组成的数字,模上k,问你是否等于c
k和c都小于10000,所以可以模拟,因为循环节不会大于10000,但是模拟很容易错,因为找到循环节的地方需要扣掉前面一段,然后各种XJB搞
模拟代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

int vis[10005];
int main(){
    int t,kase=0;
    cin>>t;
    while(t--){
        int x;
        LL m;
        int k,c;
        cin>>x>>m>>k>>c;
        int mo=0;
        mem(vis,0);
        int xun=INF;
        int tmp;
        for(int i=1;i<=m;i++){
            mo=(mo*10+x)%k;
            if(vis[mo]){
                xun=i-vis[mo];
                tmp=vis[mo];
                break;
            }
            vis[mo]=i;
        }
        kase++;
        printf("Case #%d:\n",kase);
        if(xun==INF){
            if(mo!=c) printf("No\n");
            else printf("Yes\n");
        }
        else{
            m=(m-tmp)%xun;
            int mm=mo;
            for(int i=0;i10+x)%k;
            }
            if(mm!=c) printf("No\n");
            else printf("Yes\n");
        }
    }
    return 0;
}

但是这题还有一种更加美妙的姿势,就是 xxxx 这样m个x,除以x之后就是m个1,乘9之后就是m个9,加1之后就是 10m ,所以我们可以求出 10m19×x(modk) ,判断其是否等于c,但是这样做前面需要快速幂,然后模k了之后就不能直接除以9,所以我们可以这样算 (10m1)×x(mod9k)9 ,这样就可以愉快的快速幂运算了。
ps.还有人说可以用矩阵快速幂搞,貌似也是可以的呢


1002

题意是给你n个数字,然后有些数字的位置已经固定,还有些数字的位置没有固定,让你给n个数字安排位置,使得相邻的数字的乘积和最大,有负数
首先需要记录,每个位置上是否有要求必须放第几个数
n最大范围是16,所以应该是状压,但是比赛时候我并不知道怎么dp,TAT
先确定状态 dp[i][j] ,i为这会哪些数字已经被选(二进制表示),j为最后一个选的数字是哪一个,比如i的二进制表示里面有k个1,表示1-k的位置上都已经放了数字了,j就是第k个位置上面放的数是原来数组里面的哪一个
(这个状态挺巧妙的感觉,我承认我确实没有想到,状压dp还是太渣了)
这样的话状态转移就很简单了辣

dp[i|(1<<k))][k]=max(dp[i][j]+a[j]×a[k])

假设i的二进制表示里面有x个1,那么要保证第x个位置上必须要放k或者是没有要求,否则不能转移。
代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

LL dp[1<<18][20];
int a[20];
int b[20];

int main(){
    int t,kase=0;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        mem(b,-1);
        for(int i=0;i<(1<for(int j=0;j1e18;
        }
        for(int i=0;iint p;
            scanf("%d%d",&a[i],&p);
            if(p!=-1) b[p]=i;
        }
        if(b[0]!=-1) dp[(1<0])][b[0]]=0;
        else for(int i=0;i1<0;
        for(int i=1;i<(1<for(int j=0;jif(i&(1<for(int k=0;kif((i&(1<0){
                            int pos=bits(i);
                            if(b[pos]==k||b[pos]==-1){
                                dp[i|(1<1<1e18;
        for(int i=0;i1<1][i]);
        }
        kase++;
        printf("Case #%d:\n",kase);
        cout<return 0;
}








1003

这是一道很不错的树上的题目,给你n个点的树,然后每个点有权值,有两种操作,一种是把某个点的权值修改,另一种是求一条从0开始必须经过x点的路径,使得路径上的点权和最大
因为要至少走到x,甚至要走到x的子树上,所以我们考虑可以用dfs序来解这题,因为dfs序的特性,我们维护每个点到0点的距离,这样就能很快用线段树求出在x的子树上的一点,它到0点的权值和最大
因为点权有负数,所以我们dfs的时候,dfs序只能在进入的时候++,出来的时候就不要++了,这样可以去掉很多0的节点(我的dfs序还是很弱啊,本来准备复习下,结果比赛就考了)
有了dfs序和线段树就很容易辣呀,这样就更新的时候区间加和 ya[x] ,然后 a[x]=y ,然后维护区间内最大值,就好啦
代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

struct Edge{
    int v,next;
}edge[MAX*2];
int head[MAX];
int tot;
int a[MAX];
int ti;
int p1[MAX];
int p2[MAX];
LL val[MAX];
void init(){
    mem(head,-1);
    mem(val,0);
    tot=0;
    ti=0;
}
void add_edge(int a,int b){
    edge[tot].v=b;
    edge[tot].next=head[a];
    head[a]=tot++;
}
LL sum[MAX<<2];
LL col[MAX<<2];
void pushup(int rt){
    sum[rt]=max(sum[lrt],sum[rrt]);
}
void build(int l,int r,int rt){
    col[rt]=0;
    if(l==r){
        sum[rt]=val[l];
        return;
    }
    middle;
    build(lson);
    build(rson);
    pushup(rt);
}
void pushdown(int rt,int m){
    if(col[rt]){
        col[lrt]+=col[rt];
        col[rrt]+=col[rt];
        sum[lrt]+=col[rt];
        sum[rrt]+=col[rt];
        col[rt]=0;
    }
}

void update(int l,int r,int rt,int L,int R,LL d){
    if(L<=l&&r<=R) {
        col[rt]+=d;
        sum[rt]+=d;
        return ;
    }
    middle;
    if(col[rt]!=0) pushdown(rt,r-l+1);
    if(L<=m) update(lson,L,R,d);
    if(R>m) update(rson,L,R,d);
    pushup(rt);
}

LL query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R) return sum[rt];
    if(col[rt]) pushdown(rt,r-l+1);
    middle;
    LL ret=-1e18;
    if(L<=m) ret=max(query(lson,L,R),ret);
    if(R>m) ret=max(ret,query(rson,L,R));
    return ret;
}
void dfs(int u,int fa,LL c){
    p1[u]=++ti;
    val[ti]=c;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa) continue;
        dfs(v,u,c+a[v]);
    }
    p2[u]=ti;
}

int main(){
    int t,kase=0;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        init();
        for(int i=1;iint x,y;
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            add_edge(y,x);
        }
        for(int i=0;iscanf("%d",&a[i]);
        kase++;
        printf("Case #%d:\n",kase);
        dfs(0,-1,a[0]);
        build(1,ti,1);
        while(m--){
            int op,x;
            LL y;
            scanf("%d%d",&op,&x);
            if(op==0){
                scanf("%I64d",&y);
                update(1,ti,1,p1[x],p2[x],y-a[x]);
                a[x]=y;
            }
            else printf("%I64d\n",query(1,ti,1,p1[x],p2[x]));
        }
    }
    return 0;
}

1004

额。。。不会


1005

给你一个字符串的定义,就是里面有B也有D,然后有filp操作,就是把B变成D,D变成B,然后还有反向操作,就是把字符串倒置
然后问你在区间 [L,R] 之间有多少个B
第一反应这题肯定是求 solve(R)solve(L1)
然后就是怎么求1-x之间有多少个B的问题了,我们先打表找出,每个字符串k,它的长度len和里面含有的B的个数f,

f(i)=f(i1)+1+(len(i1)f(i1))=len(i1)+1

然后对于字符串1-x,我们可以找到一条最长的,但是长度小于等于R的,完整的串k,如果长度正好为x,就可以 returnf(k)
否则就是考虑k串后面还多几个,后面是还多 xlen(k)1 个,但是考虑到这些里面的B,不就是前面完整的串结尾的D的个数么,所以我们可以把这两段里面的B放一起计算,加上中间添的一个B,就是最后长度 2×x2×len(k)1 里面有 xlen(k) 个B,然后可以递归求解前面长度为 2×len(k)+1x 的串里面有多少个B
代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           205
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

LL f[70];
LL len[70];
void init(){
    len[1]=1;
    f[1]=1;
    for(int i=2;;i++){
        len[i]=len[i-1]*2+1;
        f[i]=len[i-1]+1;
        if(len[i]>=1e18) break;
    }
}
LL solve(LL x){
    if(x==0) return 0;
    int k=1;
    for(;;k++){
        if(len[k]>x) break;
    }
    k--;
    if(len[k]==x){
        return f[k];
    }
    else return solve(2*len[k]-x+1)+x-len[k];
}

int main(){
    //freopen("in.txt","r",stdin);
    int t;
    cin>>t;
    init();
    while(t--){
        LL l,r;
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",solve(r)-solve(l-1));
    }
    return 0;
}

1006

给你n个人的编号,编号就是他的值,然后排队,每次放进去一个人的时候可以加上一个值,这个值是他和他前面的所有人里面的最小值,但是有一些限制,就是有的人必须在有的人后面。求最后得到的和的最大值
首先我们考虑和要最大,那必须是大的人先放,再放小的,但是又有限制,就好比点的入度,就是一个点必须在另外个点的前面,拓扑序,所以我们可以模拟拓扑排序,把没有限制的点放进优先队列里,然后往外面取,每次取一个就接触它对别的点的限制,然后记录一个从第一个点到它的最小值就可以了
代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

vector<int> v[MAX];
int deg[MAX];

int main(){
    int t,kase=0;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++) v[i].clear();
        mem(deg,0);
        for(int i=0;iint a,b;
            scanf("%d%d",&a,&b);
            v[a].push_back(b);
            deg[b]++;
        }
        priority_queue<int> q;
        for(int i=1;i<=n;i++){
            if(deg[i]==0) q.push(i);
        }
        int mini=INF;
        LL ans=0;
        while(!q.empty()){
            int x=q.top();q.pop();
            mini=min(mini,x);
            ans+=mini;
            for(int i=0;iif(deg[v[x][i]]==0) q.push(v[x][i]);
            }
        }
        cout<return 0;
}

你可能感兴趣的:(2016"百度之星" - 初赛(Astar Round2A)题解)