暑假D19

count

【问题描述】
李华终于逃离了无尽的英语作文, 重获自由的他对一棵树产生了兴趣。
首先,他想知道一棵树是否能分成大小相同的几块(即切掉一些边,使得每个连通块的点数相同)。然后,他觉得这个问题过于简单,于是他想知道一共有多少种方案可以把这棵树分成大小相同的几块。
然后他发现自己不会了,于是向聪明的你求助。
【输入格式】
第一行一个数,表示数的大小。
第二行至第N行,每行两个数x,y表示x和y之间有一条边。
【输出格式】
一行,表示方案数。
【样例一输入】
6
1 2
2 3
2 4
4 5
5 6
【样例一输出】
3
HINT:
对于30%的数据,1<=n<=100。
对于60%的数据,1<=n<=100000。
对于100%的数据,1<=n<=1000000。

题解

一开始就猜如果块的大小固定,那么最多有一个方案。(感性理解,反正我找不出反例)

然后考虑枚举块的大小x(n的因数),开始遍历:如果当前子树大小小于x,就不能分,传上去;否则接着遍历,最后如果大小就是x,就砍掉;大于x就不行,因为子树都是小于x的,找不到方案分出x使得当前大小也小于x,直接一直返回就行。

不过不知道为什么会选常熟有点爆炸;

看到网上的就是直接枚举,如果当前子树大小是x的倍数就砍一刀,最后砍了n/x刀就说明可以。砍一刀可以想象成把这颗子树剖离下来,变成子问题,最后肯定是分出大小为x的子树,所以要砍掉的子树大小就是x或者x的倍数。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1000006;
int n,ans,size[maxn],a[maxn];
int cnt,head[maxn];
bool opt;
struct edge{
    int x,y,next;
}e[maxn<<1];

template<class T>inline void read(T &x) {
  x=0;char ch=getchar();
  while(!isdigit(ch)) ch=getchar();
  while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

void add(int x,int y){
    e[++cnt]=(edge){x,y,head[x]};
    head[x]=cnt;
}

void dfs(int u,int fa){
    size[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].y;
        if(v==fa) continue;
        dfs(v,u);
        size[u]+=size[v];
    }
}

/*void dfs(int u,int fa,int x){
    if(size[u]<x) {a[u]=size[u];return ;}
    a[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].y;
        if(v==fa) continue;
        dfs(v,u,x);
        if(opt) return ;
        a[u]+=a[v];
    }
    if(a[u]>x) opt=true;
    else if(a[u]==x) a[u]=0;
}*/

int main(){
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    read(n);
    for(int i=1;i<n;i++){
        int x,y;
        read(x);read(y);
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
     if(!(n%i)){
         int cnt=0;
         for(int j=1;j<=n;j++) cnt+=!(size[j]%i);
         ans+=(n/i)==cnt;
     }
    printf("%d",ans);
}
View Code

Dinner

【问题描述】

清儿今天请好朋友们吃饭,一共N个人坐在坐在圆桌旁。 吃饭的第一步当然是点餐了。服务员拿来了M份菜单。第i个人阅读菜单并点出自己喜欢的菜需要花费时间T[i]。 当一个人点完菜之后,就会把菜单传到他右手边的第一个人。 M份菜单是同时发出的,每个菜单只能同时被一个人阅读。 清儿希望知道如何分发菜单,才能让点餐的总时间花费最少呢?

【输入格式】

输入文件名为dinner.in 输入第一行是N和M,表示人数和菜单数 输入第二行,N个数,表示每个人点餐所需要的时间。

【输出格式】

输出文件名为dinner.out 输出一个整数表示点餐花费的最小时间。

【样例一输入】

3 2

1 5 10

【样例一输出】

10

【样例二输入】

4 2

1 2 3 4

【样例二输出】

5

HINT:

对于20%的数据,n<=100,m<=100.

对于60%的数据,n<=10000,m<=100.

对于100%的数据,n<=50000,T[i]<=600,m<=3000.

题解

首先可以想到二分答案(需要所有段最大的最小),环就开两倍数组就好了。考虑如何check,我们肯定是先让一个人拿菜单,再将人依次加入他的队伍,如果不行了就再拿一个菜单。

可以发现我们需要指定第一个拿菜单的人,所以就只有枚举谁拿第一个菜单,对于每种情况再check,只要有一种可以就可以。

但是check不可能在想上面那样O(n)check,考虑我们是在干什么事情:其实就是划分区间,使得区间和恰满足条件。因为t>0,所以这个是满足单调性的,所以可以二分求到以每个店为开头的区间的右端点.

当然也可以用双指针。

那么对于一种情况,就从起点一直往后跳入下一个区间,如果再给定次数内跳出范围就可以。

这样就有60opts了。

因为最多还是会跳m次,所以是O(n*m*log)

还有办法优化吗?

那当然啦(干得漂亮)。我们只需要以第一个区间的点为起点跑就好了(yza dalao的思路),因为如果从后面跳,肯定会跳入第一个区间中的一个点,那么和以这个点为起点效果是一样的。

but正如上面所说的,会跳入这个区间,但是下一个点就不一定是这个区间,for一个sample:

t:1 2 3 10 3,当mid=10是,第一个区间是[1,3],那么跑出来是不可能,但是以4为起点时明显可以,因为我们跳入[1.3]时是在区间最后一个数。

所以我们就枚举起点为1...go[1]+1即可,如果go[1]=n也没关系,只是相当于再从1跑一遍。

时间复杂度O(玄学)

#include<bits/stdc++.h>
using namespace std;

const int maxn=100006;
int n,m,t[maxn];
int go[maxn];
int sum[maxn];

template<class T>inline void read(T &x) {
  x=0;char ch=getchar();
  while(!isdigit(ch)) ch=getchar();
  while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

bool check(int x,int r){
    int cnt=0;
    while(x<=r){
        x=go[x]+1;cnt++;
        if(cnt>m) return false;
    }
    return true;
}

bool check(int x){
    for(int l=1,r=1;l<=n<<1;l++){
        while(sum[r]-sum[l-1]<=x&&r<=n<<1) r++;
        go[l]=r-1;
    }
    /*for(int i=1;i<=n;i++)
     if(check(i,i+n-1)) return true;
    return false;*/
    for(int i=1;i<=go[1]+1;i++)
     if(check(i,i+n-1)) return true;
    return false;
}

int main(){
    //freopen("dinner.in","r",stdin);
    //freopen("dinner.out","w",stdout);
    read(n);read(m);
    int l=0,r=0,ans;
    for(int i=1;i<=n;i++){
        read(t[i]);
        t[i+n]=t[i];
        l=max(l,t[i]);
    }
    for(int i=1;i<=n<<1;i++) sum[i]=sum[i-1]+t[i];
    r=sum[n];
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
}
View Code

正解就比较靠谱,倍增,还是陷于处理每个点最多到哪,然后倍增f[i][j]表示从i跳2j步到哪。

O(n*log2n)


Chess

题目描述

pig在下象棋的时候特别喜欢用马,他总是计算着自己的马还需要几步才能吃掉对方的帅,以及方案数的个数,当然pig很笨,所以他只能求助于你。
我们假设在n×m的棋盘上,pig的马只能走1×2的格点,他的马一开始在stst,对方的帅在ed。当然,我们不能忽视友军和敌军,所以如果落点被友军占有,那么就不能飞过去了;如果落点被敌军占有,那么pigpig认为自己这一步赚了,所以不计入总步数。为了简化问题,我们的马在飞的时候不受到敌军限制。

输入格式

第一行两个数 m,n 表示棋盘大小为m*n。
接下来 m行 ,每n个数字,0表示格子为空,表示格子为空,1表示格子上有 敌军 ,2表 示格子上有友军 ,3表示马的位置,4表示敌方的帅

输出格式

两行。
第一行:一个数,最少情况下实际走的步数。如果没有方案存在,输出−1。
第二行:一个数,达到最小值的方案总数,如果两个方案走的空格不同则认为这两个方案不同(详见样例)。这个数保证不超过内设64位整数(longlong/int64)的大小。如果第一行是1,不要输出此行。

样例

样例输入1:

4 5
0 0 0 0 1
0 0 3 0 0
0 0 0 4 0
0 1 0 0 0

样例输出1:

0
1

样例输入2:

4 5
1 0 0 0 0
3 0 0 0 0
0 0 2 0 0
0 0 0 4 0

样例输出2:

2
3

数据范围与提示

样例2解释:

一共有3种方案。XX代表实际走的步数。

1 0 0 0 0 | 1 0 X 0 0 | 1 0 X 0 0
3 0 X 0 0 | 3 0 0 0 0 | 3 0 0 0 X
0 0 2 0 0 | 0 X 2 0 0 | 0 0 2 0 0
0 X 0 4 0 | 0 0 0 4 0 | 0 0 0 4 0

落在敌军的位置敌军就会被吃掉。不要想在两个位置来回跳跃。

数据范围:

对于30%的数据,m,n5
对于100%的数据,m,n50

题解

最短路还是比较简单,用一个bfs即可,对于第二问因为只看空白的格子上衣就比较难受,暴力的话可以考虑深搜当是答案时用map判断,map存图的局势(不算敌人)。

这样可以得30opts,但是我只有20opts,因为bfs要双端队列,加入敌人是步数没变,加到队尾单调性改变了。

正解就是忽略两个空白之间与敌人的连边,就是将他们直接相连,当然前提是中间的路径上只有敌人。

那么现在只有白点,直接跑最短路计数就好了。

至于如何实现忽略,从敌人开始遍历,将能到的白点加入队列,最后将队列里面的点两两连边。

还可以直接从空白格走,跑一遍dfs,到达的空白就是和它连边的。

当然不能连重边,还有就是敌方的帅也算敌人,所以答案要-1,那么如果初始化d为-1的话就只有特判,不然输出-2;

#include<bits/stdc++.h>
using namespace std;

#define ll long long
int n,m,sx,sy,tx,ty,num;
int mp[55][55],id[55][55],d[2505];
ll cnt[2505];
bool vis1[55][55],vis2[55][55],used[2505][2505];//vis1:敌人 used:是否连边 
int xx[8]={-1,-2,-2,-1,1,2,2,1},yy[8]={-2,-1,1,2,2,1,-1,-2};
vector<int> e[2505];

template<class T>inline void read(T &x) {
  x=0;char ch=getchar();
  while(!isdigit(ch)) ch=getchar();
  while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

int st[2505],top;
void dfs(int x,int y){
    for(int i=0;i<8;i++){
        int dx=x+xx[i],dy=y+yy[i];
        if(dx<=0||dx>n||dy<=0||dy>m) continue;
        if(!mp[dx][dy]&&!vis2[dx][dy]) {
            st[++top]=id[dx][dy];
            vis2[dx][dy]=true;
        }
        else if(mp[dx][dy]==1&&!vis1[dx][dy]) {
            vis1[dx][dy]=true;
            dfs(dx,dy);
        }
    }
}

void bfs(){
    queue<int> q;
    d[id[sx][sy]]=0;
    cnt[id[sx][sy]]=1;
    q.push(id[sx][sy]);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(unsigned int i=0;i<e[x].size();i++){
            int y=e[x][i];
            if(d[y]==-1) {d[y]=d[x]+1;q.push(y);}
            if(d[y]==d[x]+1) cnt[y]+=cnt[x];
        }
    }
}

int main(){
//    freopen("chess.in","r",stdin);
//    freopen("chess.out","w",stdout);
    read(n);read(m);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++){
         read(mp[i][j]);
         if(mp[i][j]==3) {sx=i;sy=j;mp[i][j]=0;}
         else if(mp[i][j]==4) {tx=i;ty=j;mp[i][j]=0;}
         id[i][j]=++num;
     }
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      if(!mp[i][j]){
          for(int k=4;k<8;k++){
              int dx=i+xx[k],dy=j+yy[k];
              if(dx<=0||dx>n||dy<=0||dy>m||mp[dx][dy]) continue;
              e[id[i][j]].push_back(id[dx][dy]);
              e[id[dx][dy]].push_back(id[i][j]);
              used[id[i][j]][id[dx][dy]]=used[id[dx][dy]][id[i][j]]=true;
            }
        }
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      if(mp[i][j]==1&&!vis1[i][j]){
          memset(vis2,false,sizeof(vis2));
          vis1[i][j]=true;
          dfs(i,j);
          for(int k=1;k<top;k++)
           for(int l=k+1;l<=top;l++)
            if(!used[st[k]][st[l]]){
                e[st[k]].push_back(st[l]);
                e[st[l]].push_back(st[k]);
                used[st[k]][st[l]]=used[st[l]][st[k]]=true;
                }
          top=0;
        }
    memset(d,-1,sizeof(d));
    bfs();
    if(d[id[tx][ty]]==-1) printf("-1");
    else {
        printf("%d\n",d[id[tx][ty]]-1);
        printf("%lld",cnt[id[tx][ty]]);
    }
}
/*
5 6
3 1 0 0 0 0
1 0 1 0 2 1
1 2 1 0 1 0
1 2 0 1 0 2
1 2 4 0 2 0
*/
View Code

最短路计数

#include<bits/stdc++.h>
using namespace std;

const int mod=100003;
const int maxn=1000005;
vector<int> e[maxn];
int n,m;
int d[maxn],cnt[maxn];

template<class T>inline void read(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

void bfs(){
    queue<int> q;
    q.push(1);
    cnt[1]=1;
    d[1]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(unsigned int i=0;i<e[x].size();i++){
            int y=e[x][i];
            if(d[y]==-1) d[y]=d[x]+1,q.push(y);
            if(d[y]==d[x]+1) cnt[y]=(cnt[x]+cnt[y])%mod;
        }
    }
}

int main(){
    read(n);read(m);
    for(int i=1;i<=m;i++){
        int x,y;read(x);read(y);
        e[x].push_back(y);
        e[y].push_back(x);
    }
    memset(d,-1,sizeof(d));
    bfs();
    for(int i=1;i<=n;i++) printf("%d\n",cnt[i]);
}
View Code

猜你喜欢

转载自www.cnblogs.com/sto324/p/11396399.html