zoukankan      html  css  js  c++  java
  • 2020牛客寒假算法基础集训营3

    题的质量还是好...据说sjf出的题?

    不过我还是A了7题......感觉自己水平还挺稳定的???     7 8 7?实际上是7 7 7 冥冥中自有7意。

    然后这场前期状态挺好的,一路不怎么卡,1小时5题2小时6题,都是1A,切过去挺舒服的嘻嘻。。。不过,之后就没了...

    后来想pokemon去了,一直在想怎么降n^2复杂度,(200n) 我都想到,居然没想到t差200就必能这个关键点。。。

    最后看G过的多,莽了1小时树状数组算贡献终于过了...就没啦

    果然只能2小时high-level吗 ~~~

    记录一下思维和补题。

    A.牛牛的DRB迷宫I   dp递推一下,根据当前位置的指令,他的下一个可达位都可以加上这一位的方案。话说这个他逆向题 B的idea真的好nb啊。

    H.牛牛的k合因子数 

    比赛里我是无脑写的,欧筛一遍求质数,然后处理范围内每个数的非质数因子,即(因子总数-质数因子个数-1),然后统计一下的。。。大概 n·sqrt(n)的复杂度吧。

    然后其实赛后看题解,发现这个求k因子的过程可以通过埃式筛Eratosthenes的过程直接求出来,因为埃筛是对每个数,将他的倍数都处理一遍,那么对一个非质数的倍数,那他的非质数因子都可以+1,完美符合我们的期待。比上面法好不少,而且是出题人的本意应该。

    F.牛牛的Link Power I 

    比赛里我是统计每个位置前缀的数能产生的贡献,写的有点烦了感觉。实际上三个变量就可以搞定。

    C.牛牛的数组越位   D.牛牛与二叉树的数组存储

    按题意模拟即可。酷酷酷

    I.牛牛的汉诺塔 

    我找规律做的,这种题一般都是照着他给伪代码打表,然后测小数据,找大数据递推的规律即可。发现好像根据奇偶不同每次递增有三个变量不变。然后总数分配减一下即可。

     

    G.牛牛的Link Power II

    我树状数组(线段树差不多应该)维护贡献做的,开两个树状数组,分别表示每个位置的信息(其实就是这个位置的下标),每个位置的1(是否有1).

    然后你要支持修改,和动态维护ans。对每个变更的位置,产生的影响只有这个位置的1带来的贡献,没错吧?

    那对于将0改成1的pos,增加的贡献是多少?肯定是 前面的1产生的贡献  + 后面的1产生的贡献。将1改成0的位置也一模一样的,减去即可。

    那他们分别是多少呢?

    我们记前面的1的个数为l ,后面的1显然是总数-l,记作r = cnt1-l;

    然后我们记录的位置信息就有用了,其实为啥要记录位置,还要求他们的前缀和?这是因为我们需要每个1和他们前面和后面1之间的相对信息,他们的差就是他们之间产生的贡献。

    前面的1产生的贡献是       l*pos - pos前缀的sum 

    后面的1产生的贡献是      pos后缀(不包括pos本身)的sum-r*pos

    大致推出上面两个式子,这题就结束啦。

    然后比赛里也观(xia)察(gao)了好久。感觉对了,式子就推出来了。还是靠玄学?所以我才要总结一下啊......

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=2e5+7;
    10 const int INF=0x3f3f3f3f;
    11 const int mod=1e9+7;
    12  
    13 struct BIT
    14 {
    15     int sum[maxn];
    16     void clear(){memset(sum,0,sizeof sum);}
    17     int lowbit(int x){return x & (-x);}
    18     void add(int x,int val)
    19     {
    20         while (x < maxn)
    21         {
    22             sum[x] += val;
    23             sum[x]%=mod;
    24             x += lowbit(x);
    25         }
    26     }
    27     ll query(int x)
    28     {
    29         if (x <= 0) return 0;
    30         ll res = 0;
    31         while (x)
    32         {
    33             res += sum[x];
    34             res%=mod;
    35             x -= lowbit(x);
    36         }
    37         return res;
    38     }
    39     ll query(int l,int r){return query(r) - query(l -1);}
    40 }bit,bit2;
    41  
    42 char s[maxn];
    43  
    44 int main()
    45 {
    46     ios::sync_with_stdio(false);
    47     cin.tie(0);
    48     int n;
    49     cin>>n>>s+1;
    50     ll sum=0,cnt1=0,ans=0;
    51     for(int i=1;i<=n;++i)
    52     {
    53         sum+=cnt1;
    54         sum%=mod;
    55         if(s[i]=='1')
    56         {
    57             ans+=sum;
    58             ans%=mod;
    59             cnt1++;
    60             bit.add(i,i);
    61             bit2.add(i,1);
    62         }
    63     }
    64     cout<<ans<<endl;
    65     int q;
    66     cin>>q;
    67     while(q--)
    68     {
    69         int op,pos;
    70         cin>>op>>pos;
    71         int l=bit2.query(pos);
    72         int r=cnt1-l;
    73         if(op==1)
    74         {
    75             ans+=((1ll*l*pos)%mod-bit.query(pos) +mod)%mod;
    76             ans=(ans+mod)%mod;
    77             ans+=(bit.query(pos+1,n)-(1ll*r*pos)%mod+mod) % mod;
    78             ans=(ans+mod)%mod;
    79             bit.add(pos,pos);
    80             bit2.add(pos,1);
    81             cnt1++;
    82         }
    83         else
    84         {
    85             ans-=((1ll*l*pos)%mod-bit.query(pos) +mod)%mod;
    86             ans=(ans+mod)%mod;
    87             ans-=(bit.query(pos+1,n)-(1ll*r*pos)%mod+mod)%mod;
    88             ans=(ans+mod)%mod;
    89             bit.add(pos,-pos);
    90             bit2.add(pos,-1);
    91             cnt1--;
    92         }
    93         cout<<ans<<'
    ';
    94     }
    95     return 0;
    96 }

    J.牛牛的宝可梦Go

    出题人真的已经疯狂在提示了,就是不会出现t一样的。

    然后我也想到了floyd处理完最短路,对宝可梦的时间排序后,只要两个宝可梦之间的时间差不小于他们位置最短的距离,答案就可以变的更大。

    然后我就一直在和n^2斗智斗勇,试图降低复杂度。

    我也注意到了n=200,图最多200个点,很少,唉,那我们对每个宝可梦,处理200个点咋样?你还是要每个点的最大的且t和pos都满足条件的值呀...想歪啦。

    然后正解是因为每个t不一样,n最多200,那么只要时间间隔超过n,这两点之间必能够在这段时间中跑到,所以你最多只需要遍历当前之前200个宝可梦,是否满足条件。

    对于更之前的宝可梦,他们之间的t之差必然>=n,就必然可达了。

    所以就有了200n的做法,对于更之前的,只需要记录一个最大值即可。

    但这里要注意,似乎会存在不可达的点,dp要初始为-INF,为0的话,对于起点不可达的点,也进行扩展,是不可取的。而dp[0]=0,从这里扩展是可以的。

    这里我估计我自己写得WA穿都想不到。原图没保证一定联通。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=2e5+7;
    10 const int INF=0x3f3f3f3f;
    11 const int MOD=1e9+7;
    12  
    13 int mp[210][210];
    14 ll dp[maxn];
    15  
    16 struct node
    17 {
    18     int p;
    19     ll t,val;
    20 }a[maxn];
    21  
    22 bool cmp(node a,node b){return a.t<b.t;}
    23  
    24 int main()
    25 {
    26     ios::sync_with_stdio(false);
    27     cin.tie(0);
    28     int n,m;
    29     cin>>n>>m;
    30     memset(mp,0x3f,sizeof(mp));
    31     while(m--)
    32     {
    33         int u,v;
    34         cin>>u>>v;
    35         mp[u][v]=mp[v][u]=1;
    36     }
    37     for(int i=1;i<=n;++i) mp[i][i]=0;
    38     for(int k=1;k<=n;++k)
    39     {
    40         for(int i=1;i<=n;++i)
    41         {
    42             for(int j=1;j<=n;++j)
    43                 mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
    44         }
    45     }
    46     int k;
    47     cin>>k;
    48     for(int i=1;i<=k;++i)
    49         cin>>a[i].t>>a[i].p>>a[i].val;
    50     sort(a+1,a+1+k,cmp);
    51     a[0].t=0;
    52     a[0].p=1;
    53     ll ans=0;
    54     ll premax=0;
    55     for(int i=1;i<=k;++i)
    56     {
    57         if(i>200)
    58         {
    59             premax=max(premax,dp[i-200]);
    60             dp[i]=a[i].val+premax;
    61         }
    62         else
    63             dp[i]=-INF;
    64         for(int j=1;j<=200 && i>=j;++j)
    65         {
    66             if(mp[a[i].p][a[i-j].p]<=a[i].t-a[i-j].t)
    67                 dp[i]=max(dp[i],dp[i-j]+a[i].val);
    68         }
    69         ans=max(ans,dp[i]);
    70     }
    71     cout<<ans<<endl;
    72     return 0;
    73 }

    B.牛牛的DRB迷宫II

    好有趣的构造题啊啊啊。

    构造一个二进制码生成器,辣是zdnb。

    就是你发现(实际上不能发现)

    图转载自题解:https://ac.nowcoder.com/discuss/365306?type=101&order=0&pos=8&page=2

    你可以用三对角线构造出2的幂次,然后的哪个位上你需要1,你就可以让这一位联通到n,m,也就是该图中的竖线横线的意思(我是把对角线之下这一格‘R’改成‘B’,向下也联通)。

    否则就别把这个二进制幂次加入答案即可,就别动即可。

    然后自己构造一下即可。

    我的代码还把第31行最右边堵上了,因为(1<<30)已经>1e9+7了,不可能需要这里了。然后就按位联通即可。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int MAXN=2e5+7;
    10 const int INF=0x3f3f3f3f;
    11 const int MOD=1e9+7;
    12 
    13 char mp[55][55];
    14 ll dp[100][100];
    15 
    16 int main()
    17 {
    18     ios::sync_with_stdio(false);
    19     cin.tie(0);
    20     int n=32,m=32;
    21     for(int i=1;i<=n;++i)
    22     {
    23         for(int j=1;j<=m;++j)
    24         {
    25             if(i==n || i==n-1 && j>=i) mp[i][j]='R';
    26             else if(i==j) mp[i][j]='B';
    27             else if(i==j+1) mp[i][j]='R';
    28             else mp[i][j]='D';
    29         }
    30     }
    31     int k;
    32     cin>>k;
    33     for(int i=1;i<32;++i)
    34     {
    35         if(k&(1ll<<(i-1)))
    36             mp[i+1][i]='B';
    37     }
    38     cout<<n<<' '<<m<<endl;
    39     for(int i=1;i<=n;++i)
    40         cout<<mp[i]+1<<endl;
    41     return 0;
    42 }

     E.牛牛的随机数

    数位dp的解法没学。学的按位统计的做法。

    这也是道 按二进制位算贡献的题。暴力算不了啊,由于异或运算的特殊性,可以快速统计。好吧以后看见位运算大数据的题,一定要想这种运算特别的性质。然后尝试按位统计???

    原因在于异或的答案一次贡献最多只有1,每个区间的cnt0 * 另一个区间cnt1 * 这位的权值,再交换一下两个区间算一遍这位就好了。

    观察知道二进制每位的01有循环节,就可以很快的算0~x,有多少个这位上的1,这位上的0。(感觉这个已经很nb了)

    我忽然想到好像只需要一个函数,总-多少个1,不就是0嘛。不过也差不多。

    然后这里学的题解 容斥写法,不过好像可直接统计  r1的区间-(l1-1)的区间的1和0数就对了,也差不多。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=2e5+7;
    10 const int inf=0x3f3f3f3f;
    11 const int mod=1e9+7;
    12 
    13 ll getsum0(ll x,int bit)
    14 {
    15     x++;
    16     ll ans=(x/(2ll<<bit)*(1ll<<bit))%mod;
    17     if((x/(1ll<<bit))&1) ans+=(1ll<<bit);
    18     else ans+=x%(1ll<<bit);
    19     return ans%mod;
    20 }
    21 
    22 ll getsum1(ll x,int bit)
    23 {
    24     x++;
    25     ll ans=(x/(2ll<<bit)*(1ll<<bit))%mod;
    26     ans+=((x/(1ll<<bit))&1)*(x%(1ll<<bit));
    27     return ans%mod;
    28 }
    29 
    30 ll getval(ll lim1,ll lim2)
    31 {
    32     ll ans=0;
    33     for(int i=0;i<=60;++i)
    34     {
    35         ll tmp=(1ll<<i)%mod;
    36         ans+=getsum0(lim1,i)*getsum1(lim2,i)%mod*tmp%mod+getsum0(lim2,i)*getsum1(lim1,i)%mod*tmp%mod;
    37         ans%=mod;
    38     }
    39     return ans;
    40 }
    41 
    42 ll quick(ll x,ll n)  //快速幂 x^n
    43 {
    44     ll res=1;
    45     x%=mod;
    46     if(x==0) return 0; //减少特判防止倍数次
    47     while(n)
    48     {
    49         if(n&1) res=(res*x)%mod;
    50         x=(x*x)%mod;
    51         n>>=1;
    52     }
    53     return res;
    54 }
    55 ll inv(ll a)   //逆元  费马小定理,要求 a,mod互素
    56 {
    57     return quick(a,mod-2);
    58 }
    59 int main()
    60 {
    61     ios::sync_with_stdio(false);
    62     cin.tie(0);
    63     int t;
    64     cin>>t;
    65     while(t--)
    66     {
    67         ll l1,r1,l2,r2;
    68         cin>>l1>>r1>>l2>>r2;
    69         ll ans=0;
    70         ans+=getval(r1,r2);
    71         ans-=getval(l1-1,r2);
    72         ans-=getval(l2-1,r1);
    73         ans+=getval(l1-1,l2-1);
    74         ans=(ans%mod+mod)%mod;
    75         ans=ans*inv(((r1-l1+1)%mod)*((r2-l2+1)%mod))%mod;
    76         cout<<ans<<endl;
    77     }
    78     return 0;
    79 }

    终于写(水)完了,逃去LOL~

  • 相关阅读:
    MF研究:TinyCLR运行时原理
    不到600美元,即可获取一套MF Porting kit 3.0
    SideShow Gadget开发[1]
    我眼中的WinHEC大会台前幕后
    Visual C++ 2008入门经典 第四章数组 字符串(二) 简单
    PHP的常用函数 简单
    Visual C++ 2008入门经典 第五章程序结构(二) 简单
    用PHP将Unicode 转化为UTF8 简单
    sql GROUP_CONCAT(... SEPARATOR) 简单
    Visual C++ 2008入门经典 第四章数组 字符串(练习题) 简单
  • 原文地址:https://www.cnblogs.com/Zzqf/p/12287963.html
Copyright © 2011-2022 走看看