• NOIP模拟测试19


    T1:

      题目大意:将一颗有N个节点的树分割,使得每个联通块大小相等,问一共有多少方案。(N<=1000000)

      首先,一条很显然的性质,每个联通块的大小一定是N的因子。

      然后,我们可以对于每个因子d,DFS一遍,维护一个si值,代表该子树中有多少节点是连通的,一旦这个值等于d,将这颗子树切掉,若这个值大于d,则判定不合法。

      这个方法每次DFS只验证一个因子,效率较低。

      我们可以只进行一遍DFS将每颗子树的大小存进一个桶里。一颗子树被切掉,当且仅当该子树中剩下的点共有d个,若d合法,则当前子树原有的节点数应为d的倍数,我们可以枚举每个因子的每个倍数,在桶里查询,若总数为N/d,则合法。

      时间复杂度为$sum_{d|N}d=O(Nlog_2N)$

    Code:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 using namespace std;
     6 const int N=1000010;
     7 int n,nm=0,m=0,ans=1;
     8 int fi[N],d[210],s[N];
     9 struct edge{
    10     int v,ne;
    11 }e[N<<1];
    12 void add(int x,int y)
    13 {
    14     e[++nm].v=y;
    15     e[nm].ne=fi[x];fi[x]=nm;
    16 }
    17 int read()
    18 {
    19     int s=0;char c=getchar();
    20     while(c<'0'||c>'9') c=getchar();
    21     while(c>='0'&&c<='9'){
    22         s=(s<<3)+(s<<1)+c-'0';
    23         c=getchar();
    24     }
    25     return s;
    26 }
    27 void divide()
    28 {
    29     for(int i=2;i<=sqrt(n);i++){
    30         if(n%i==0){
    31             d[++m]=i;
    32             if(i*i!=n) d[++m]=n/i;
    33         }
    34     }
    35 }
    36 bool dfs(int x,int p)
    37 {
    38     s[x]=1;
    39     for(int i=fi[x];i!=0;i=e[i].ne){
    40         int y=e[i].v;
    41         if(y==p) continue;
    42         dfs(y,x);
    43         s[x]+=s[y];
    44     }
    45 }
    46 bool check(int x)
    47 {
    48     int tot=0;
    49     for(int i=x;i<=n;i+=x){
    50         int l=lower_bound(s+1,s+n+1,i)-s;
    51         int r=upper_bound(s+1,s+n+1,i)-s;
    52         tot+=r-l;
    53     }
    54     if(x*tot==n) return true;
    55     else return false;
    56 }
    57 int main()
    58 {
    59     n=read();
    60     if(n!=1) ans++;
    61     for(int i=1;i<n;i++){
    62         int x=read(),y=read();
    63         add(x,y);add(y,x);
    64     }
    65     divide();
    66     dfs(1,0);
    67     sort(s+1,s+n+1);
    68     for(int i=1;i<=m;i++){
    69         if(check(d[i])) ans++;
    70     }
    71     printf("%d
    ",ans);
    72     return 0;
    73 }
    T1

    T2:

      题目大意:一个圆环上有N个数,将这N个数分为M个块,使得最大的块的和最小,输出这个最小值。

      显然的单调性,二分答案。

      对于圆环,短环成链并复制一倍。

      关键点在于起始点的选择,$O(N)$枚举起始点再$O(N)$判断是绝对不可行的,我们需要优化。

      我们可以优化判断,一般的判断方式是一个一个累加,直到大于二分的值位置,这个过程可以用倍增优化,设st[i][j]为以i为起点,长度为2j的区间和,从大到小枚举j,若加和小于二分的值,则累加。

      时间复杂度$O(NM*log_2NM)$,M较大时有可能被卡掉。

      还有一种思路是优化枚举起点。

      我们先假设以0处为起始点,用$O(N)$的方法先找到此时的第一个块,也就是最靠左的块,并设这个块的左端点为L,右端点为R。

      那么枚举的起始点只用在区间[L,R]内移动。

      证明:

        由于是圆环,所以从一个点向左右扩展都是可以的,如果以R+1为起始点,它可以向左扩展,其向左的第一个块必定为[L,R],与枚举起始点在L的情况重复了,所以不需要再枚举,其它点同理。

      所以我们就在复杂度上除了一个M。

      但是这个方法在一开始二分的值较大时会被卡成$O(N)$,所以在一开始要强化剪枝,一旦发现可行情况或发现不可行时要立即中止,才能保证算法的高效

      时间复杂度$O((N^2log_2N)/M)$,M较小时会十分低效。

      将两种思路综合起来就很难被卡掉了,或者可以测试点分治。我用的是第二种思路。

    Code:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 const int N=50010;
     5 int n,m,nn,L=0,R=0,ans;
     6 int t[N<<1];
     7 bool judge(int y,int x)
     8 {
     9     int ans=1,tot=0;
    10     for(int i=y;i<=y+n-1;i++){
    11         if(tot+t[i]>x){
    12             ans++;tot=t[i];
    13         }
    14         else tot+=t[i];
    15         if(ans>m) return false;
    16     }
    17     return true;
    18 }
    19 bool check(int x)
    20 {
    21     int l=1,r=0,tot=0;
    22     for(int i=1;i<=n;i++){
    23         if(tot+t[i]>x) break;
    24         r=i;tot+=t[i];
    25     }
    26     for(int i=l;i<=r;i++){
    27         if(judge(i,x)) return true;
    28     }
    29     return false;
    30 }
    31 int main()
    32 {
    33     scanf("%d%d",&n,&m);
    34     nn=2*n-1;
    35     for(int i=1;i<=n;i++){
    36         scanf("%d",&t[i]);
    37         R+=t[i];L=max(L,t[i]);
    38     }
    39     for(int i=n+1;i<=nn;i++)
    40         t[i]=t[i-n];
    41     ans=R;
    42     while(L<=R){
    43         int mid=(L+R)>>1;
    44         if(check(mid)){
    45             ans=mid;
    46             R=mid-1;
    47         }
    48         else
    49             L=mid+1;
    50     }
    51     printf("%d
    ",ans);
    52     return 0;
    53 }
    T2

    T3:

      详见洛谷1606白银莲花池。

      我打的是BFS和DFS,用spfa也可以。

      首先要求起点到终点的最短路径。我们发现移动一步的贡献不大于1,用不着最短路算法,一般的BFS可以解决。

      但是此题有两种边权,0和1,并且0在使用一次后会变成1。

      性质一:起点到终点的最短路中没有环。

      证明一:环的权值不为负,所以走环之后步数不会少。

      性质二:同一个点至多被经过一次。

      证明二:若同一个点被经过多次,则一定存在环。

      我们可以记录每个点是否被走过,然后就可以BFS了。

      到现在这道题和算法竞赛进阶指南0x26中电路维修一样。可以用双端队列BFS解决,若到一个点的贡献为0,则从队头入队,反之从队尾入队。

      问题一解决,现在求路径条数。

      数据有问题,不过在作者和某NC的竭力hack,以及tdcp提供错误AC标程,该问题已被解决。

      我们发现如果一个点被重复经过,会导致结果的错误,我们需要一个强有力的剪枝。

      我们把从起点开始的求最短路BFS改为从终点开始,求出终点到所有点的最短距离,作为估价函数,搜索中一旦发现当前代价加估价大于最短路时,立即终止搜索,这样就可以防止重复了。

      题目要求经过空格不同才算方案不同。

      这个用BFS不好解决,我们可以嵌套DFS。

      性质三:所有连通的敌人可以缩为一个点。

      我们发现敌人就是一个中介点,由于敌人和方案数无关,它不能被入队,但是我们可以经过敌人到达其他的空地。

      我没建边,所以用了鬼畜的DFS。

      大框架还是BFS,每次对出队元素进行DFS,如果遇到敌人,则继续进行DFS,遇到空格就将其入队。

      注意在同一次DFS中,更新节点的信息来源于同一个节点,所以一个点只能被更新一次。多次DFS之间,为防止重复,每个点只能入队一次。

      BFS到最后,即为正确答案。

      由于DFS的存在,时间复杂度$O(N^2M^2)$,但远远达不到这个上界。

    Code:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<queue>
      4 #define LL long long
      5 using namespace std;
      6 const int N=61;
      7 int n,m,sx,sy,ex,ey;
      8 int ans1=0;
      9 LL ans2=0;
     10 int a[N][N],v[N][N];
     11 LL dp[N][N][N*N];
     12 bool vis[N][N],in[N][N],out[N][N][N*N];
     13 int dex[9]={0,1,1,2,2,-1,-1,-2,-2};
     14 int dey[9]={0,2,-2,1,-1,2,-2,1,-1};
     15 deque<int> q1,q2,q3;
     16 void bfs1()
     17 {
     18     v[ex][ey]=1;
     19     q1.push_back(ex);q2.push_back(ey);
     20     while(!q1.empty()){
     21         int x=q1.front(),y=q2.front();
     22         q1.pop_front();q2.pop_front();
     23         for(int i=1;i<=8;i++){
     24             int xx=x+dex[i],yy=y+dey[i];
     25             if(a[xx][yy]==2||xx<1||xx>n||yy<1||yy>m) continue;
     26             if(v[xx][yy]!=0) continue;
     27             if(a[xx][yy]==1){
     28                 v[xx][yy]=v[x][y];
     29                 q1.push_front(xx);q2.push_front(yy);
     30             }
     31             else{
     32                 v[xx][yy]=v[x][y]+1;
     33                 q1.push_back(xx);q2.push_back(yy);
     34             }
     35         }
     36     }
     37     for(int i=1;i<=n;i++){
     38         for(int j=1;j<=m;j++){
     39             v[i][j]-=1;
     40             if(a[i][j]==3) ans1=v[i][j];
     41         }
     42     }
     43 }
     44 void dfs1(int xx,int yy,int x,int y,int z)
     45 {
     46     vis[xx][yy]=true;
     47     for(int i=1;i<=8;i++){
     48         int nex=xx+dex[i],ney=yy+dey[i];
     49         if(a[nex][ney]==2||nex<1||nex>n||ney<1||ney>m) continue;
     50         if(a[nex][ney]==1){
     51             if(v[nex][ney]+z>ans1) continue;
     52             if(vis[nex][ney]) continue;
     53             dfs1(nex,ney,x,y,z);
     54         }
     55         else{
     56             if(v[nex][ney]+z+1>ans1) continue;
     57             if(in[nex][ney]) continue;
     58             in[nex][ney]=true;
     59             dp[nex][ney][z+1]+=dp[x][y][z];
     60             if(!out[nex][ney][z+1]){
     61                 q1.push_back(nex);q2.push_back(ney);q3.push_back(z+1);
     62                 out[nex][ney][z+1]=true;
     63             }
     64         }
     65     }
     66 }
     67 void dfs2(int x,int y,int z)
     68 {
     69     vis[x][y]=false;
     70     for(int i=1;i<=8;i++){
     71         int nex=x+dex[i],ney=y+dey[i];
     72         if(a[nex][ney]==2||nex<1||nex>n||ney<1||ney>m) continue;
     73         if(a[nex][ney]==1){
     74             if(v[nex][ney]+z>ans1) continue;
     75             if(!vis[nex][ney]) continue;
     76             dfs2(nex,ney,z);
     77         }
     78         else{
     79             if(v[nex][ney]+z+1>ans1) continue;
     80             in[nex][ney]=false;
     81         }
     82     }
     83 }
     84 void bfs2()
     85 {
     86     while(!q1.empty()){
     87         q1.pop_back();q2.pop_back();
     88     }
     89     dp[sx][sy][0]=1;out[sx][sy][0]=true;
     90     q1.push_back(sx);q2.push_back(sy);q3.push_back(0);
     91     while(!q1.empty()){
     92         int x=q1.front(),y=q2.front(),z=q3.front();
     93         out[x][y][z]=false;
     94         q1.pop_front();q2.pop_front();q3.pop_front();
     95         dfs1(x,y,x,y,z);
     96         dfs2(x,y,z);
     97     }
     98     ans2=dp[ex][ey][ans1];
     99 }
    100 int main()
    101 {
    102     scanf("%d%d",&n,&m);
    103     for(int i=1;i<=n;i++){
    104         for(int j=1;j<=m;j++){
    105             scanf("%d",&a[i][j]);
    106             if(a[i][j]==3){
    107                 sx=i;sy=j;
    108             }
    109             if(a[i][j]==4){
    110                 ex=i;ey=j;
    111             }
    112         }
    113     }
    114     bfs1();
    115     if(ans1==-1){
    116         printf("%d
    ",ans1);
    117         return 0;
    118     }
    119     else
    120         printf("%d
    ",ans1-1);
    121     bfs2();
    122     printf("%lld
    ",ans2);
    123     return 0;
    124 }
    T3
  • 相关阅读:
    selenium手机百度搜索
    selenium模拟手机浏览器
    selenium模拟登录赶集网,手动解决验证码问题
    selenium模拟登录京东,手动解决验证码问题,抓取购物车价格
    selenium模拟登录QQ空间,手动解决验证码问题
    两数之和&N数之和(求教!)
    Linux内核设计与实现——从内核出发
    Linux内核简介
    编程规范
    GIT基础使用
  • 原文地址:https://www.cnblogs.com/hz-Rockstar/p/11347492.html
Copyright © 2020-2023  润新知