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

猜你喜欢

转载自www.cnblogs.com/idyllic/p/11839225.html
今日推荐