• codeforces 597 div 2


    A

    题意:

    有无限个方块,上面分别是0,1,2.....。若方块i可以被表示为ax+by(x,y>0),则方块为白色,否则为黑色,给出a,b。问黑方块是否有无限个。

    分析:

    1:若(a,b)=1,由斐蜀等式,则存在an+bm=1(n,m为整数(不保证大于0))。我们只要考虑一个ab区间,若区间内都是白的,那么一定后面都是白色。

    我们考虑2|abnm|到2|abnm|+ab-1,这个区间内显然,2|abnm|为ax+by的形式,之后每加一都是加an+bm,而由于从2|abnm|(|nmb|a+|nma|b)开始,累加后an+bm每个数的x‘,y’恒为正。

    所以若(a,b)=1,则黑方块一定有限。

    2:若(a,b)=k>1,则非k的倍数一定是黑方块,黑方块无穷。

    综上,只要求(a,b)就行了。若为1输出Finite,否则为Infinite。

     1 #include<stdio.h>
     2 #include<iostream>
     3 using namespace std;
     4 int gcd(int x,int y)
     5 {
     6     int r=x%y;
     7     while (r!=0)
     8     {
     9         x=y;
    10         y=r;
    11         r=x%y;
    12     }
    13     return y;
    14 }
    15 int main()
    16 {
    17     int t,k,a,b;
    18     scanf("%d",&t);
    19     for (k=1;k<=t;k++)
    20     {
    21         scanf("%d%d",&a,&b);
    22         if (gcd(a,b)==1) printf("Finite\n");
    23             else printf("Infinite\n");
    24     }
    25     return 0;
    26 }
    View Code of A

    B:

    题意:

    告诉你你能出a个石头,b个布,c个剪刀。(n=a+b+c)

    告诉对面这n回合的出法,你要赢round(n/2)回合(平局和输随便啦,只看赢得场数)

    问能否做到,不能输出”NO“,能输出YES,并在下一行输出一种可行的方法(R,P,S一行输出)

    分析:

    就如果它出剪刀,你就看你有没有石头出,有就出呗。。。因为对着前后的不同剪刀出石头是等效的,贪心就行了。

    代码:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cmath>
     4 using namespace std;
     5 int main()
     6 {
     7     double n;
     8     int i,k,t,a,b,c,s;
     9     char ch;
    10     int ans[101];
    11     scanf("%d",&t);
    12     for (k=1;k<=t;k++)
    13     {
    14         scanf("%d",&a);
    15         scanf("%d%d%d",&a,&b,&c);
    16         getchar();
    17         n=a+b+c;
    18         s=0;
    19         memset(ans,0,sizeof(ans));
    20         for (i=1;i<=n;i++)
    21         {
    22             ch=getchar();
    23             if (ch=='R'&&b>0) 
    24             {
    25                 b--;
    26                 s++;
    27                 ans[i]=2;
    28             }
    29             if (ch=='P'&&c>0) 
    30             {
    31                 c--;
    32                 s++;
    33                 ans[i]=3;
    34             } 
    35             if (ch=='S'&&a>0)
    36             {
    37                 a--;
    38                 s++;
    39                 ans[i]=1;
    40             }    
    41         }
    42         if (s<round(n/2)) printf("NO\n");
    43         else
    44         {
    45             printf("YES\n");
    46             for (i=1;i<=n;i++)
    47             {
    48                 if (ans[i]==1) printf("R");
    49                 else if (ans[i]==2) printf("P");
    50                 else if (ans[i]==3) printf("S");
    51                 else 
    52                 {
    53                     if (a>0) 
    54                     {
    55                         printf("R");
    56                         a--;
    57                     }
    58                     else if (b>0)
    59                     {
    60                         printf("P");
    61                         b--;
    62                     }
    63                     else
    64                     {
    65                         printf("S");
    66                         c--; 
    67                     }
    68                 }
    69             }
    70             printf("\n"); 
    71         }
    72     }
    73     return 0;
    74 }
    View Code of B

    C:

    题意:

    给你一个打印机打的小写字符组成的字符串,已知打印机会把w打成uu,m打成nn。问这个字符串可能的原串有几种。

    分析:

    很容易想到DP,因为就一个水的不行的线性DP,如果这个点和前面两个点都是u或者v就是前两项和,不然是前一项。不过有个wa点,出现w和m绝对NG!不过样例给出来了就是了。

    代码:

     1 #include<iostream>
     2 #include<string>
     3 #include<cstring>
     4 using namespace std;
     5 long long dp[100010],flag,l,i;
     6 int main()
     7 {
     8     string str;
     9     getline(cin,str);
    10     l=str.length();
    11     flag=0;
    12     for (i=0;i<l;i++)
    13     {
    14         if (str[i]=='m'||str[i]=='w') {flag=1;break;}
    15     }
    16     if (flag==1) cout<<0<<endl;
    17     else
    18     {
    19         dp[0]=1;
    20         if ((str[0]=='u'&&str[1]=='u')||(str[0]=='n'&&str[1]=='n')) dp[1]=2;
    21             else dp[1]=1;
    22         for (i=2;i<l;i++)
    23         {
    24             if ((str[i]=='u'&&str[i-1]=='u')||(str[i]=='n'&&str[i-1]=='n'))
    25                 dp[i]=(dp[i-2]+dp[i-1])%1000000007;
    26             else dp[i]=dp[i-1];
    27         }
    28         cout<<dp[l-1]<<endl;
    29     }
    30     return 0;
    31 }
    View Code of C

    D:

    题意:

    有n(0<n<=1e6)个城镇,每个坐标是(Xi,Yi),现在要给每个城镇通上电,对于城镇i,有两种方法,第一种自己发电,代价是Ci,第二种是连到一个有电的城市j,代价是(Ki+Kj)*Dij,Dij表示城镇i和城镇j之间的曼哈顿距离(横坐标差绝对值加纵坐标差绝对值)

    分析:

    构造一个虚拟的有电城市0,然后自己发电就是连接到0,从而变成了最小生成树,先生成所有边,然后kruskal一下。

    代码:

      1 #include<iostream>
      2 #include<stdio.h>
      3 #include<algorithm>
      4 #include<cstring>
      5 struct node
      6 {
      7     long long u,v,dis;
      8 };
      9 struct node d[2002010];
     10 long long x[2010],y[2010],c[2010],k[2010];
     11 long long ans2[2010][2],ans1[2010],f[2010];
     12 using namespace std;
     13 long long find(long long a)
     14 {
     15     if (a==f[a]) return a;
     16     else return find(f[a]);
     17 }
     18 void combine(long long a,long long b)
     19 {
     20     f[find(a)]=find(b);
     21 }
     22 int cmp(struct node a,struct node b)
     23 {
     24     if (a.dis==b.dis&&a.u==b.u) return a.v<b.v;
     25     else if (a.dis==b.dis) return a.u<b.u;
     26     else return a.dis<b.dis;
     27 }
     28 int main()
     29 {
     30     long long n,i,m,res1,res2,sum,j,u,v;
     31     scanf("%I64d",&n);
     32     for (i=1;i<=n;i++)
     33     {
     34         scanf("%I64d%I64d",&x[i],&y[i]); 
     35     }
     36     m=0;
     37     for (i=1;i<=n;i++)
     38     {
     39         scanf("%I64d",&c[i]);
     40         m++;
     41         d[m].dis=c[i];
     42         d[m].u=0;
     43         d[m].v=i;
     44     }
     45     for (i=1;i<=n;i++)
     46     {
     47         scanf("%I64d",&k[i]);
     48     }
     49     for (i=1;i<=n-1;i++)
     50     for (j=i+1;j<=n;j++)
     51     {
     52         m++;
     53         d[m].dis=(abs(x[i]-x[j])+abs(y[i]-y[j]))*(k[i]+k[j]);
     54         d[m].u=i;
     55         d[m].v=j;            
     56     }
     57     sort(d+1,d+m+1,cmp);
     58 //    for (i=1;i<=m;i++)
     59 //    {
     60 //        cout<<d[i].u<<" "<<d[i].v<<" "<<d[i].dis<<endl;
     61 //    }
     62     for (i=0;i<=n;i++)
     63     {    
     64         f[i]=i;
     65     }
     66     res1=0;
     67     res2=0;
     68     sum=0;
     69  
     70     for (i=1;i<=m;i++)
     71     {
     72         u=d[i].u;
     73         v=d[i].v;
     74         if (find(u)!=find(v))
     75         {
     76             sum=sum+d[i].dis;
     77             combine(u,v);
     78             if (u==0)
     79             {
     80                 res1++;
     81                 ans1[res1]=v;
     82             }
     83             else
     84             {
     85                 res2++;
     86                 ans2[res2][0]=u;
     87                 ans2[res2][1]=v;
     88             }    
     89         }
     90         if (res1+res2==n) break;
     91     }
     92     printf("%I64d\n",sum);
     93     printf("%I64d\n",res1);
     94     for (i=1;i<res1;i++)
     95     {
     96         printf("%I64d ",ans1[i]);
     97     }
     98     printf("%I64d\n",ans1[res1]);
     99     printf("%I64d\n",res2);
    100     for (i=1;i<=res2;i++)
    101     {
    102         printf("%I64d %I64d\n",ans2[i][0],ans2[i][1]);
    103     }
    104     return 0;
    105 }
    106  
    View Code of D

    E:

    题意:

    有个10*10的方格,一个人从(10,1)蛇形(先横向前,再向上一格再横向后,再向上一格)向终点(1,1)前进。

    每次前进都会丢一个1-6的骰子(几率相同),决定这次前进几步,如果到重点还有k步,投的大于k就会作废。

    当然,不会这么简单,这个方格上存在一些梯子,可以竖着向上走一些长度。当你回合末停在一个梯子下的时候你可以选择向上爬(也可以不爬),下回合将在梯子顶的格子开始。需要注意的是,一个格子顶多有一个向上的梯子,不过可以有很多梯子通往这个格子。另爬梯子上来之后不能接着爬(毕竟回合末才能爬)

    现在想尽可能快的走到终点(也就是中间选择爬不爬梯子将取决于那个更快),问总回合数的期望是多少。

    分析:

    概率DP,正着来的话,梯子的问题很难处理,不过每个格子只有一个向上的梯子,所以很容易想到倒退(概率DP常见手法)

    需要注意的有:不是每一步都是除以6,有些点数不可能。

    代码:

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<cstring>
     4 #include<string>
     5 #include<cmath>
     6 using namespace std;
     7 double dp[12][12];//[2];
     8 //double dp2[12][12][2];
     9 int a[12][12];
    10 int c[12][12];
    11 int h[101];
    12 int l[101];
    13 int main()
    14 {
    15     int i,j,k,n,t;
    16     double sum;
    17     for (i=1;i<=10;i++)
    18     for (j=1;j<=10;j++)
    19     {
    20         cin>>a[i][j];
    21     }
    22     for (i=1;i<=10;i++)
    23     for (j=1;j<=10;j++)
    24     {
    25         h[i*10+j-10]=i;
    26         if (i%2) l[i*10-10+j]=j;
    27             else l[i*10-10+j]=11-j;
    28         c[h[i*10+j-10]][l[i*10+j-10]]=i*10+j-10;
    29     }
    30     //for (i=1;i<=10;i++)
    31     //for (j=1;j<=10;j++)
    32     //{
    33     //    cout<<c[i][j]<<" ";
    34     //    if (j==10) cout<<endl;
    35     //}
    36     dp[1][1]=0;
    37     for (k=2;k<=100;k++)
    38     {
    39         sum=0;
    40         n=0;
    41         for (t=1;t<=6;t++)
    42         {
    43             if (k>t)
    44             {
    45                 if (a[h[k-t]][l[k-t]]!=0)
    46                 {
    47                     if (dp[h[k-t]][l[k-t]]>dp[h[c[h[k-t]-a[h[k-t]][l[k-t]]][l[k-t]]]][l[c[h[k-t]-a[h[k-t]][l[k-t]]][l[k-t]]]])
    48                         sum=sum+dp[h[c[h[k-t]-a[h[k-t]][l[k-t]]][l[k-t]]]][l[c[h[k-t]-a[h[k-t]][l[k-t]]][l[k-t]]]];
    49                     else sum=sum+dp[h[k-t]][l[k-t]];
    50                 }
    51                 else sum=sum+dp[h[k-t]][l[k-t]];
    52                 n++;
    53             }
    54             else break;
    55         }
    56         dp[h[k]][l[k]]=(sum+6)/n;
    57     }
    58     printf("%.10f\n", dp[10][1]);
    59     return 0;
    60 }
    View Code of E

    F:

    题意:

    t(最多100)组询问,每组询问为问区间[l,r],中间有多少组数a,b满足a+b=a^b(0<=l<=r<=1e9)

    分析:

    乍一看傻眼了,不过经验性的一个处理区间。

    ans=solve(r,r)-solve(l-1,r)*2+solve(l,l);

    solve(a,b)表示第一个数从1-a,第二个数从1-b

    然后是按二进制位嘛,我下意识的想到的思路是这样的,找到两个数二进制数位中最高的那个位,

    然后如果两个数这位都是1,那么,可以拆分成3个子情况递归,分别是

    1:第一个数那位为1,第二个数那个位为0。

    2:第一个数那位为0,第二个数那个位为1。

    3:第一个数那位为0,第二个数那个位也为0。

    然后我duang duang的敲了个代码,修了下过了样例。。。。。。。的前两个,然后第三个居然跳不出结果。咋回事呢?

    我一拍脑袋,这玩意根本不降低多少复杂度,某些情况我可能反而天才的提升了复杂度(不愧是我)

    怎么改进呢,很明显递归重复计算了很多相同的子情况,很容易想到记录下来空间换时间。

    我意识到,递归的子情况无非是a变成a-(1<<i),min(a,(1<<i)-1),也就是要么是去掉首位,要么是变成111....1,除了1111...1,之外,其他都是取原a的从后往前的若干位。b同理。

    所以用dp[i][j][k]表示状态,i是二进制位信息,从30-0,jk分别用0,1表示a,b变成啥状态。就是个结合位运算的dp。

    代码:

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<cstring>
     4 using namespace std;
     5 long long dp[32][2][2];
     6 long long  solve(long n, long m)
     7 {
     8     long long a,b,c,d;
     9     memset(dp,0,sizeof(dp));
    10     dp[31][0][0] = 1;
    11     for (int i = 30; i >= 0; --i)
    12     {
    13         for (a = 0; a <= 1; a++)
    14             for (b = 0; b <= 1; b++)
    15                 for (c = 0; c <= 1; c++)
    16                     for (d = 0; d <= 1-c; d++)
    17                         if ((a|| c <= (n >> i & 1)) && (b || d <= (m >> i & 1)))
    18                             dp[i][a | (c < (n >> i & 1))][b | (d < (m >> i & 1))] += dp[i+1][a][b];
    19     }
    20     return dp[0][1][1];
    21 }
    22 int main()
    23 {
    24     long long i,j,t,k,l,r,ans;
    25     cin>>t;
    26     for (k=1;k<=t;k++)
    27     {
    28         cin>>l>>r;
    29         ans=solve(r+1,r+1)-solve(l,r+1)*2+solve(l,l);
    30         cout<<ans<<endl;
    31     }
    32     return 0;
    33 }
    View Code of F
  • 相关阅读:
    Centos7.2安装MariaDB数据库,并进行基础配置
    Web安全之环境搭建
    PHP构建一句话木马
    Spark2.1.0安装
    Spark2.1.0编译
    cdh-5.10.0搭建安装
    八、频繁模式挖掘Frequent Pattern Mining
    七、特征提取和转换
    六、降维
    五、聚类
  • 原文地址:https://www.cnblogs.com/idyllic/p/11839225.html
Copyright © 2020-2023  润新知