zoukankan      html  css  js  c++  java
  • UPC2018组队训练赛第一场

    题目来自 ICPC2017 Urumqi


    A题:Coins I

    最优的策略一定是:当有至少k 枚硬币面朝下时,则选k 枚面朝下的硬币去抛掷(任意k 枚都可以);如果不足k枚面朝下,则在选择所有面朝下的硬币的基础上再额外选择若干面朝上的银币。

    于是有动态规划,记F[i][j]表示抛掷了i 次后,有j 枚硬币面朝上的概率。他们应该满足F[i][0]+F[i][1]+...+F[i][n]=1。转移时,考虑从当前状态(i,j)出发,抛掷的k 枚硬币的所有可能结果:分别有0~k 枚面朝上。其中k 枚硬币抛掷后有l 枚面朝上的概率为C(k,l)/2k。时间复杂度O(nmk)。

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 typedef long long ll;
     5 const int MAX=1000;
     6 ll qmod(ll a,ll b)
     7 {
     8     ll ans=1;
     9     while(b)
    10     {
    11         if(b&1) ans=ans*a;
    12         a*=a;
    13         b>>=1;
    14     }
    15     return ans;
    16 }
    17 double c[200][200],p[200];
    18 void init()
    19 {
    20     c[0][0]=1;
    21     for(int i=1;i<106;i++)
    22     {
    23         c[i][0]=1;
    24         for(int j=1;j<=i;j++)
    25             c[i][j]=c[i-1][j-1]+c[i-1][j];
    26     }
    27     p[0]=1;
    28     for(int i=1;i<=106;i++)
    29         p[i]=p[i-1]/2;
    30 }
    31 int main()
    32 {
    33     int T,n,m,t;
    34     init();
    35     for(scanf("%d",&T);T;T--)
    36     {
    37         scanf("%d%d%d",&n,&m,&t);
    38         double dp[200][200];
    39         memset(dp,0,sizeof(dp));
    40         dp[0][0]=1;
    41         for(int i=0;i<m;i++)
    42         {
    43             for(int j=0;j<=n;j++)
    44             {
    45                 if(dp[i][j]==0)  continue;
    46                 for(int k=0;k<=t;k++)
    47                 {
    48                     if(n-j>=t)  dp[i+1][j+k]+=dp[i][j]*c[t][k]*p[t];
    49                     else    dp[i+1][j-(t-(n-j))+k]+=dp[i][j]*c[t][k]*p[t];
    50                 }
    51             }
    52         }
    53         double ans=0;
    54         for(int i=1;i<=n;i++)
    55             ans+=dp[m][i]*i;
    56         printf("%.3lf
    ",ans);
    57     }
    58     return 0;
    59 }
    View Code

    B题:The Difference

    四个数A1、A2、A3、A4。从小到大排序后输出A3*A4-A1*A2即可


    D题:Fence Building

    在一个圆内有n个点,求连接任意两个点后,最多形成的区域数

    最优的策略是在选定n个点后,两两连线,且满足任意三条线不交于一点。

    本题需要用到欧拉公式:在平面图中,F=E-V+2,其中V是所有顶点数,E是边数,F是面数。(注意,本题需要减去外面的“无线面”)

    任意四个点,会形成一个交点,并贡献额外的2条独立线段。所以V=n+C(n,4), E=n+2C(n,4)+C(n,2). (n是n段圆弧,2C(n,4)是一个交点所贡献的额外的2条独立线段,C(n,2)是n个点所能形成的边的个数),所以F=C(n,2)+C(n,4)+1.

    本题的数据给的是1e18,所以对于取模运算要特别小心,防止超出longlong

     1 #include <iostream>
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 typedef long long ll;
     5 const ll mod=1e9+7;
     6 ll extend_gcd(ll a, ll b, ll &x, ll &y)
     7 {
     8     if (b == 0)
     9     {
    10         x = 1, y = 0;
    11         return a;
    12     }
    13     else
    14     {
    15         ll r = extend_gcd(b, a % b, y, x);
    16         y -= x * (a / b);
    17         return r;
    18     }
    19 }
    20 ll inv(ll a, ll n)//求逆元
    21 {
    22     ll x, y;
    23     extend_gcd(a, n, x, y);
    24     x = (x % n + n) % n;
    25     return x;
    26 }
    27 int main()
    28 {
    29     int t;
    30     ll n;
    31     scanf("%d",&t);
    32     for(int cas=1; cas<=t; cas++)
    33     {
    34         scanf("%lld",&n);
    35         if(n==1||n==2)
    36         {
    37             printf("Case #%d: %lld
    ",cas,n);
    38             continue;
    39         }
    40         if(n==3)
    41         {
    42             printf("Case #%d: %lld
    ",cas,n+1);
    43             continue;
    44         }
    45         ll b1=n%mod,b2=(n-1)%mod,b3=(n-2)%mod,b4=(n-3)%mod;//特别注意取模
    46         ll a2=(b1*b2)%mod;
    47         ll a3=(a2*b3)%mod;
    48         ll a4=(a3*b4)%mod;
    49         ll n4=(a4%mod*inv(24,mod)%mod)%mod;
    50         ll n2=(a2%mod*inv(2,mod)%mod)%mod;
    51         ll ans=n4+n2+1+mod;
    52         ans%=mod;
    53         printf("Case #%d: %lld
    ",cas,ans);
    54     }
    55     return 0;
    56 }
    View Code

    G题:The Mountain

    给你一连串的点的坐标,保证x1<x2<...<xn,y1=0,yn=0 求这些坐标依次连线后与x轴所围成的面积,首先先求一下第一个和最后一个三角形,再依次求中间的梯形的面积。


     H题:Count Numbers

    记F[i]表示数位和为i 的不含0 整数有多少个,再记G[i]表示数位和为i 的不含0 整数的和。那么F[i] = sum F[i-j]、G[i] = sum 10G[i-j]+jF[i-j],其中j 枚举了最低位的数字,并从1 遍历到9。

    不妨同时维护相邻的9 个位置的值(共18 个),则可以构造18 阶矩阵A,实现它的快速转移。整个问题的答案也对应了A 的ab 次幂。可以利用矩阵乘法的结合律实现它的快速计算。

    参考https://www.cnblogs.com/tian-luo/p/9515011.html

    可以用_int128来存储ab,关键是要能想到构造矩阵,该构造什么样的矩阵

      1 #include <iostream>
      2 #include<bits/stdc++.h>
      3 using namespace std;
      4 typedef long long ll;
      5 ll p=1e9+7;
      6 int add(int a,int b)     //加法取模
      7 {
      8     return a+b>=p?a+b-p:a+b;
      9 }
     10 int mul(ll a,int b)     //乘法取模
     11 {
     12     return a*b%p;
     13 }
     14 struct Mat      //定义矩阵
     15 {
     16     int v[18][18];
     17     Mat()
     18     {
     19         memset(v,0,sizeof(v));
     20     }
     21     void init()     //单位矩阵
     22     {
     23         for(int i=0;i<18;i++)
     24         {
     25             v[i][i]=1;
     26         }
     27     }
     28 };
     29 Mat operator *(Mat x,Mat y)
     30 {
     31     Mat z;
     32     for(int i=0;i<18;i++)  //新的写法,节省计算0的时间
     33     {
     34         for(int j=0;j<18;j++)
     35         {
     36             if(x.v[i][j])
     37             {
     38                 for(int k=0;k<18;k++)
     39                 {
     40                     if(y.v[j][k])
     41                     {
     42                         z.v[i][k]=add(z.v[i][k],mul(x.v[i][j]%p,y.v[j][k]%p));
     43                     }
     44                 }
     45             }
     46         }
     47     }
     48     return z;
     49 }
     50 Mat Matqmod(Mat a,__int128 b)  //矩阵快速幂
     51 {
     52     Mat c;
     53     c.init();
     54     while(b)
     55     {
     56         if(b&1)
     57         {
     58             c=c*a;
     59         }
     60         a=a*a;
     61         b>>=1;
     62     }
     63     return c;
     64 }
     65 int main()
     66 {
     67     ll cnt[15]={0},sum[15]={0};
     68     cnt[0]=1;
     69     for(int i=1;i<=9;i++)   //初始化1~9的cnt[]和sum[]
     70     {
     71         for(int j=1;j<=i;j++)
     72         {
     73             cnt[i]+=cnt[i-j];
     74             sum[i]+=10*sum[i-j]+j*cnt[i-j];
     75         }
     76     }
     77     Mat a,b;
     78     a.v[0][0]=10;
     79     for(int i=1;i<9;i++)    //构造矩阵 a 
     80     {
     81         a.v[0][i]=10;
     82         a.v[i][i-1]=1;
     83     }
     84     for(int i=9;i<18;i++)
     85     {
     86         a.v[0][i]=i-8;
     87         a.v[9][i]=1;
     88     }
     89     for(int i=10;i<18;i++)
     90     {
     91         a.v[i][i-1]=1;
     92     }
     93      
     94     int t;
     95     scanf("%d",&t);
     96     while(t--)
     97     {
     98         ll n,m;
     99         scanf("%lld %lld %lld",&n,&m,&p);
    100         for(int i=0;i<9;i++)    //构造矩阵 b 
    101         {
    102             b.v[i][0]=sum[9-i]%p;
    103         }
    104         for(int i=9;i<18;i++)
    105         {
    106             b.v[i][0]=cnt[18-i]%p;
    107         }
    108         __int128 now=n;
    109         for(int i=2;i<=m;i++) //用_int128计算a的b次方
    110         {
    111             now=now*(__int128)n;
    112         }
    113         if(now<=9)      //now<=9直接输出
    114         {
    115             printf("%lld
    ",sum[now]%p);
    116             continue;
    117         }
    118         Mat c=Matqmod(a,now-9)*b;   //now>9 套用矩阵
    119         printf("%lld
    ",c.v[0][0]);
    120     }
    121     return 0;
    122 }
    123  
    View Code

     I题:A Possible Tree

    带权并查集   参考https://blog.csdn.net/mitsuha_/article/details/80418507

    有一棵树,首先给你c个关系,每个关系表示u和v之间的异或和为val,问你在这个关系中,从第一个开始依次往下最多能同时满足多少个。

    记r[i]为节点i到根的路径上所有边的异或和,则每一个关系中实际上给出了r[u]xorx[v]的值

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 typedef long long ll;
     5 const int MAX=1e5+10;
     6 int fa[MAX],n,c,t;
     7 ll r[MAX];
     8 void init()
     9 {
    10     for(int i=0;i<=n;i++)
    11         fa[i]=i,r[i]=0;
    12 }
    13 int Get(int x)
    14 {
    15     if(x==fa[x])    return x;
    16     int fx=fa[x];
    17     fa[x]=Get(fa[x]);
    18     r[x]^=r[fx];
    19     return fa[x];
    20 }
    21 int main()
    22 {
    23 //    freopen("in.txt","r",stdin);
    24     for(scanf("%d",&t);t;t--)
    25     {
    26         scanf("%d%d",&n,&c);
    27         int a,b;
    28         for(int i=1;i<n;i++)
    29             scanf("%d%d",&a,&b);
    30         init();
    31         int ans=c+1;            //这里要令ans=c+1
    32         int x,y;
    33         ll z;
    34         for(int i=1;i<=c;i++)
    35         {
    36             scanf("%d%d%lld",&x,&y,&z);
    37             if(ans!=c+1)  continue;
    38             int fx=Get(x),fy=Get(y);
    39             if(fx==fy&&(r[x]^r[y])!=z)  ans=i;
    40             else
    41             {
    42                 fa[fx]=fy;
    43                 r[fx]=r[x]^r[y]^z;
    44             }
    45         }
    46         printf("%d
    ",ans-1);  //这里注意是ans-1
    47     }
    48     return 0;
    49 }
    View Code

     K题:Sum of Line

    参考https://blog.csdn.net/hao_zong_yin/article/details/81613218

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 const int MAX=1e4+10;
     5 const int mod=998244353;
     6 typedef long long ll;
     7 ll qmod(ll a,ll b)      //快速幂,后面求逆元的时候会用到
     8 {
     9     ll ans=1;
    10     while(b)
    11     {
    12         if(b&1) ans=(ans*a)%mod;
    13         a=a*a%mod;
    14         b>>=1;
    15     }
    16     return ans;
    17 }
    18 bool is_prime[MAX];  //判断是不是质数
    19 int prime[MAX],prime_cnt;       //prime[]用来存储质数,prime_cnt表示质数的个数
    20 void getPrime()         //素数筛
    21 {
    22     prime_cnt=0;
    23     is_prime[0]=is_prime[1]=1;
    24     for(int i=2;i<MAX;i++)
    25     {
    26         if(!is_prime[i])
    27         {
    28             prime[prime_cnt++]=i;
    29             for(int j=2;j*i<MAX;j++)
    30                 is_prime[j*i]=1;
    31         }
    32     }
    33 }
    34 int t,k,get_p[50];  //get_p[]存储分解的质因数
    35 int main()
    36 {
    37     getPrime();
    38     for(scanf("%d",&t);t;t--)
    39     {
    40         scanf("%d",&k);
    41         int n=k;
    42         ll sum=(ll)k%mod*(k+1)%mod*(2*k+1)%mod*qmod(6,mod-2)%mod%mod;   //所有数的平方和
    43         int p_cnt=0;
    44         for(int i=0;i<prime_cnt;i++)
    45         {
    46             if(prime[i]>n)  break;
    47             if(k%prime[i]==0)   //分解质因数
    48             {
    49                 get_p[p_cnt++]=prime[i];
    50                 while(k%prime[i]==0)    k/=prime[i];
    51             }
    52         }
    53         if(k>1) get_p[p_cnt++]=k;
    54         ll ans=0;
    55         for(int i=1;i<(1<<p_cnt);i++)       //利用二进制进行容斥
    56         {
    57             ll x=1;
    58             int cnt=0;
    59             for(int j=0;j<p_cnt;j++)
    60             {
    61                 if((i>>j)&1)
    62                 {
    63                     x=x*get_p[j]%mod;
    64                     cnt++;
    65                 }
    66             }
    67             k=n/x;
    68             if(cnt&1)   ans=(ans+(ll)x*x%mod*k%mod*(k+1)%mod*(2*k+1)%mod*qmod(6,mod-2)%mod)%mod;        //奇数个相加
    69             else        ans=((ans-(ll)x*x%mod*k%mod*(k+1)%mod*(2*k+1)%mod*qmod(6,mod-2)%mod)%mod+mod)%mod;  //偶数个相减
    70         }
    71         ans=((sum-ans)%mod+mod)%mod;  //减去不合法的,即为最终合法的
    72         printf("%lld
    ",ans);
    73     }
    74     return 0;
    75 }
    View Code
    如有错误,请指正,感谢!
  • 相关阅读:
    【阿里聚安全·安全周刊】苹果证实 iOS 源代码泄露|英国黑客赢下官司
    150万元重奖!阿里软件供应链安全大赛正式启动
    【阿里聚安全·安全周刊】山寨外挂有风险养蛙需谨慎|健身追踪热度图爆军事基地位置
    移动APP外挂攻防实战
    阿里云正式上线移动直播问答解决方案,助力APP尽情“撒币”!
    阿里安全资深专家杭特辣评中国网络安全人才之“怪现状”
    【阿里聚安全·安全周刊】Intel芯片级安全漏洞事件|macOS存在漏洞
    阿里聚安全年终盘点|2017互联网安全领域十大话题
    【技术分析】DowginCw病毒家族解析
    独家探寻阿里安全潘多拉实验室,完美越狱苹果iOS11.2.1
  • 原文地址:https://www.cnblogs.com/scott527407973/p/9513237.html
Copyright © 2011-2022 走看看