• 牛客2019湘潭大学程序竞赛


    官方题解

      都是中文题不需要翻译,话说,我居然sb的在a题看错题目错了3发

      ABD签到题,D题贪心思维,没啥好说的

    CMath Problem

      我觉得这题是我卡了最久的题,数学不好推不出来,然后打了个表发现的,官方题解里有证明为什么a=192*q+1,q是自然数。知道这个之后就很好做了,我们就可以求出在r范围内有多少符合的数,和l-1范围内有多少符合的数做差就可以了。像对于x,我们先求出它范围里有多少个192的倍数,就是q=x/192,然后a是192*q+1,如果x刚好是q的倍数,那么q就去掉一个1个,然后是求∑a,而范围内192的倍数有q个,∑a就是192*(1+2+3+..+q)+q+1

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 typedef long long ll;
     5 ll solve(int x)
     6 {
     7     if(!x)
     8         return 0;
     9     ll q=x/192;
    10     if(x%192==0)
    11         q--;
    12     return 192ll*q*(1+q)/2+q+1;//+1是对1这个情况特判 
    13 }
    14 int main()
    15 {
    16     int t,l,r;
    17     scanf("%d",&t);
    18     while(t--)
    19     {
    20         scanf("%d%d",&l,&r);
    21         printf("%lld
    ",solve(r)-solve(l-1));
    22     }
    23     return 0;
    24 }
    来一份192的题

    FBlack & White

      一开始感觉没啥思路,想要贪心但是很容易举出反例,但模拟了一下之后有了个类似尺取的思路。因为尺取有个左端点的调整,但我不确定这个左端点该如何调整,所以我改成了枚举左端点,然后去模拟。

      具体思路就是,先把字符串分成连续的01子串,比如0001110011就可以分成4个字串,然后相应的权值就是它们的长度,v[0]=3,v[1]=3,v[2]=2,v[3]=2,这样的话,每个子串的下一个子串就是和它不同的需要翻转,而下下个就是和它相同的,不需要翻转。然后就枚举一个子串v[i]作为头部,然后就模拟看最多翻转m个字符,它可以延伸的长度,详情见代码。

     1 #include<cstdio>
     2 #include<vector>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=100118;
     6 char s[N];
     7 vector<int> v;
     8 int main()
     9 {
    10     int t,n,m;
    11     scanf("%d",&t);
    12     while(t--)
    13     {
    14         scanf("%d%d",&n,&m);
    15         scanf("%s",s);
    16         v.clear();
    17         int sum=0,ans=0,len;
    18         for(int i=0;s[i]!='';i++)
    19         {
    20             sum++;
    21             if(s[i]!=s[i+1])
    22             {
    23                 v.push_back(sum);
    24                 ans=max(ans,sum);
    25                 sum=0;
    26             }
    27         }
    28         for(int i=0,j,mm;i<v.size();i++)
    29         {
    30             len=v[i];//枚举第i个子串作为头部 
    31             j=i+1; 
    32             mm=m;
    33             while(j<v.size()&&mm>=v[j])//如果存在下个子串,并且翻转次数可以把它全翻转过来 
    34             {
    35                 len+=v[j];//全翻转过来,长度拼接上 
    36                 mm-=v[j];//翻转次数减少 
    37                 if(j+1<v.size())//如果存在下下个子串 
    38                     len+=v[j+1];//长度直接拼接上 
    39                 j+=2;//下一个要翻转的位置 
    40             }
    41             if(j<v.size())//如果还有下一个子串 
    42                 len+=mm;//那么说明剩余的翻转次数不足以把v[j]全部翻转
    43                 //但可以翻转v[j]的前mm个使得len更长 
    44             ans=max(ans,len);
    45         }
    46         printf("%d
    ",ans);
    47     }
    48     return 0;
    49 }
    翻翻转转都转不清

    Hchat

      没看出是分组背包,一开始时就觉得像dp,但感觉三个循环可能会超时,没敢写,后来没其他想法了,还是试了一下。首先定义的dp[i][j]就是到第i天时,生气值为j的最少在线时间,那么如果我们知道第i天的生气值为j的最少在线时间b[i][j],那么转移过程就很好转移了,就是dp[i][j+k]=min(dp[i][j],dp[i-1][j]+b[i][k]),意思就是枚举到第i-1天生气值为j的情况,以及第i天生气值为k的情况,那么到第i天生气值为j+k的情况就相应更新。

      而b[i][j],怎么处理出来呢,我们先处理个a[i][j]为,第i天前j个小时的生气值的前缀和,然后对于每一天i,我们枚举个上线时间j和下线时间k,这样在线时间就是k-j+1,而在这段时间上线会造成的生气值就是a[i][m]-(a[i][k]-a[i][j-1]),相应地更新就好,详情见代码。

     1 #include<cstdio>
     2 #include<vector>
     3 #include<algorithm>
     4 using namespace std;
     5 const int inf=0x3f3f3f3f;
     6 int dp[250][250],a[250][250],b[250][250];
     7 char s[250];
     8 int main()
     9 {
    10     int t,n,m,K;
    11     scanf("%d",&t);
    12     while(t--)
    13     {
    14         scanf("%d%d%d",&n,&m,&K);
    15         for(int i=0;i<=n;i++)
    16             for(int j=0;j<=K;j++)//前面值初始化m,wrong了三发 
    17                 dp[i][j]=b[i][j]=inf;
    18         for(int i=1;i<=n;i++)
    19         {
    20             scanf("%s",s+1);
    21             for(int j=1;j<=m;j++)//处理前缀和 
    22                 a[i][j]=a[i][j-1]+s[j]-'0';
    23         }
    24         int sq;
    25         for(int i=1;i<=n;i++)
    26         {
    27             sq=a[i][m];//当天最多累计的生气值是sq
    28             for(int j=sq;j<=K;j++)
    29                 b[i][j]=0;//那么对于sq及之后的生气值最少上线时间都是0 
    30             for(int j=1;j<=m;j++)//枚举上线时间 
    31                 for(int k=j;k<=m;k++)//枚举下线时间 
    32                 {
    33                     sq=a[i][m]-(a[i][k]-a[i][j-1]);//如果在这个时间段下线,当天累计的生气值 
    34                     b[i][sq]=min(b[i][sq],k-j+1);
    35                 }
    36         }
    37         dp[0][0]=0;
    38         for(int i=1;i<=n;i++)
    39             for(int j=0;j<=K;j++)//枚举前i-1天,生气值为j的情况 
    40                 for(int k=0;j+k<=K;k++)//枚举第i天,生气值为k的情况 
    41                     dp[i][j+k]=min(dp[i][j+k],dp[i-1][j]+b[i][k]); //更新前i天,生气值为j+k的情况 
    42         int ans=inf;
    43         for(int i=0;i<=K;i++)
    44             ans=min(ans,dp[n][i]);
    45         printf("%d
    ",ans);
    46     }
    47     return 0;
    48 }
    爱的魔力转圈圈

    GTruthman or Fakeman

      感觉像是一个种类并查集,但并查集的话,我不懂怎么处理让说谎的人最少,所以当时是想到了深搜的方法,但一直觉得时间复杂度很大,就没有写。结束后在qdcxk的点拨下,仔细想想时间复杂度大概就O(5*m),并不是我想的n方。

      首先我们把关系视为边来建图,这样就可以得到好几个连通块,而在一个个连通块中,要是有一个人的身份确定了,那么这个连通块中的所有人的身份都确定了,因为一个人不是诚实的就是说谎的。所以我们就可以在每个连通块随便挑一个人,分别让他是0(说谎的),和1(诚实的),然后记录两个情况下连通块中说谎的人数量,然后挑少的那个情况来对这个连通块的所有人确定身份,这样的话对每个块挑的那个人搜一遍0的情况,然后恢复回未知身份,再搜一遍1的情况,然后恢复回未知身份,最后确定身份,一个连通块最多就进行了5次深搜,详情见代码。

      1 #include<cstdio>
      2 const int N=101108,M=101108;
      3 struct Side{
      4     int v,ne,w;
      5 }s[M<<1];
      6 int n,m,sn,head[N],sf[N];
      7 void init()
      8 {
      9     sn=0;
     10     for(int i=0;i<=n;i++)
     11         sf[i]=head[i]=-1; //-1代表还未明确身份 0代表欺骗者,1代表老实人 
     12 }
     13 void add(int u,int v,int w)
     14 {
     15     s[sn].v=v;
     16     s[sn].w=w; 
     17     s[sn].ne=head[u];
     18     head[u]=sn++;
     19 }
     20 void dfs(int u,int w,int &num)
     21 {
     22     if(num==-1)
     23         return ;
     24     if(w==0)
     25         num++;
     26     sf[u]=w;
     27     for(int i=head[u];~i;i=s[i].ne)
     28     {
     29         int v=s[i].v;
     30         if(sf[v]==-1)//只有u是1且u说v是1时以及u是0且u说v是0时,v才是1 
     31             dfs(v,((w^s[i].w)^1),num);
     32         else if(sf[v]!=((w^s[i].w)^1))
     33         {//如果身份以已经明确的身份冲突,那么这种情况就不可能 
     34             num=-1;
     35             return ;
     36         }
     37     }
     38 }
     39 void clear(int u)//清空身份 
     40 {
     41     sf[u]=-1;
     42     for(int i=head[u];~i;i=s[i].ne)
     43     {
     44         int v=s[i].v;
     45         if(sf[v]!=-1)
     46             clear(v);
     47     }
     48 } 
     49 int main()
     50 {
     51     int t,u,v,w;
     52     scanf("%d",&t);
     53     while(t--)
     54     {
     55         scanf("%d%d",&n,&m);
     56         init();
     57         while(m--)
     58         {
     59             scanf("%d%d%d",&u,&v,&w);
     60             add(u,v,w);
     61             add(v,u,w);//这里要建双向边,否则u和v可能会在不同的连通块 
     62 //            由  u v w
     63 //                1 1 1
     64 //                1 0 0
     65 //                0 1 0
     66 //                0 0 1
     67 //            u说v是w,那么v就应该说u是w    
     68         }
     69         int num0,num1,is=1;
     70         for(int i=1;i<=n;i++)
     71             if(sf[i]==-1)
     72             {
     73                 num0=num1=0;
     74                 //分别假设一遍i是0或1的情况,并记录下相应的欺骗者的个数
     75                 dfs(i,0,num0); 
     76                 clear(i);//因为还没明确是什么身份,清空回未知状态 
     77                 dfs(i,1,num1);
     78                 clear(i);
     79                 //看那种情况下的欺骗者的个数比较少,来确定相应的身份 
     80                 if(num0!=-1&&num1!=-1)
     81                 {
     82                     if(num0<=num1)
     83                         dfs(i,0,num0);
     84                     else
     85                         dfs(i,1,num1);
     86                 }
     87                 else if(num0!=-1)//只可能是i是0的情况 
     88                     dfs(i,0,num0);
     89                 else if(num1!=-1)//只可能是i是1的情况 
     90                     dfs(i,1,num1);
     91                 else//都不可能,也就是有矛盾的产生 
     92                 {
     93                     is=0;
     94                     break;
     95                 }
     96             }
     97         if(is)
     98         {
     99             for(int i=1;i<=n;i++)
    100                 printf("%d",sf[i]);
    101         }
    102         else
    103             printf("-1");
    104         printf("
    ");
    105     }
    106     return 0;
    107 }
    真真假假

     EWatermelon

      这题的话,没注意到数据范围,m最大才1e6,完全可以模拟吃西瓜的过程,不过当时我想到的就是一个二分的方法,可惜没写,啊,看来有想法就果断去写。

      首先,二分个什么呢,二分吃了多少轮西瓜,因为lililalala每次都是吃a[p]个西瓜(p是lililalala所在的下标),而其他人最少吃1个,最多吃a[i[个,那么第k轮的时候到lililalala,西瓜最多剩下maxm=m-(k*(n-1+a[p])+p-1)个,最少剩下minm=m-(k*sum[n]+sum[p-1]) ,(sum是前缀和)那么如果minm>0,就说明西瓜没吃完,这第k轮并不能让到lililalala吃时没西瓜吃,k得增大,而如果maxm<0就说明这第k轮还没到lililalala吃西瓜就吃完了,k得减小,而minm<=0&&maxm>=0就说明到lililalala吃之前西瓜是够吃的,而且可以控制到lililalala吃的时候,西瓜刚好没有了。

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 typedef long long ll;
     5 const int N=100118;
     6 int n,p,a[N]={0};
     7 ll m,sum[N]={0};
     8 int check(ll k)
     9 {
    10     ll maxm=m-(k*(n-1+a[p])+p-1);
    11     ll minm=m-(k*sum[n]+sum[p-1]);
    12     if(minm>0)//没吃完 
    13         return 1;
    14     else if(maxm<0)//不够吃 
    15         return -1;
    16     else
    17         return 0;
    18 }
    19 int main()
    20 {
    21     int t;
    22     scanf("%d",&t);
    23     while(t--)
    24     {
    25         scanf("%d%lld",&n,&m);
    26         p=0;
    27         for(int i=1;i<=n;i++)
    28         {
    29             scanf("%lld",&a[i]);
    30             if(a[i]>a[p])
    31                 p=i;
    32             sum[i]=sum[i-1]+a[i];
    33         }
    34         if(n==1)
    35         {
    36             printf("YES
    ");
    37             continue;
    38         }
    39         int flag=1;
    40         ll l=0,r=(m-p+1)/(n-1+a[p]);
    41         while(l<=r)
    42         {
    43             ll mid=(l+r)>>1;
    44             flag=check(mid);
    45             if(flag==1)
    46                 l=mid+1;
    47             else if(flag==-1)
    48                 r=mid-1;
    49             else
    50                 break;
    51         }
    52         if(!flag)
    53             printf("YES
    ");
    54         else
    55             printf("NO
    ");
    56     }
    57     return 0;
    58 }
    我也想吃西瓜
  • 相关阅读:
    遗忘
    2008年第一篇1.15
    键盘对应值
    油田开采基础知识
    最近
    oracle中lob数据的操作
    [转]大数据能做什么,大数据和云是不是一回事?
    [转]从这些方面判断一家公司的好坏
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    谈创新
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10816405.html
Copyright © 2020-2023  润新知