zoukankan      html  css  js  c++  java
  • Codeforces #447 Div.2 Tutorial

    Problem A:QAQ

    给一个字符串,求出可非连续的QAQ序列有多少个。

    Analysis:比较水的一道题,记录每一个Q的位置,预处理A的个数即可

                  然而还是fst了,原因是未考虑一个Q都没有的极端情况,导致vector().size-1溢出!

                  以后一定要注意,当对unsigned int类型的vector().size作减法时考虑是否可能出现负数情况

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 int cnt[105];
     5 string s; 
     6 vector<int> q;
     7 
     8 int main()
     9 {
    10     cin >> s;
    11     for(int i=0;i<s.size();i++)
    12     {
    13         cnt[i+1]=cnt[i];
    14         if(s[i]=='A') cnt[i+1]++;
    15         else if(s[i]=='Q') q.push_back(i+1);
    16     }
    17     
    18     if(!q.size()) cout << 0,return 0; //对极端情况要特殊考虑 
    19                           //因为vector.size()是unsigned int,减1时会溢出 
    20     int res=0;
    21     for(int i=0;i<q.size()-1;i++)
    22         for(int j=i+1;j<q.size();j++)
    23         {
    24             int x=q[i],y=q[j];
    25             res+=(cnt[y]-cnt[x]);
    26         }
    27     cout << res;
    28     
    29     return 0;
    30 }
    Problem A

     Problem B:Ralph and his magic field

    喜闻乐见的组合数学题,可划归为:在一个n*m的表格上填0和1,问在每行每列的和均为奇数或偶数时总方案数为多少

    Analysis:一道比较考思维的题目,而思维能力不足一直我的弱项

                  其实看到n,m为1e18就知道是结论题,这时候推不出来暴力打表找规律也行啊,以后还是要灵活一些

                 正解就是把问题转换成(n-1)*(m-1)的全排列问题,最后的一行一列根据最终要求推出来就行了

                 但要注意的是,当要求为奇数,且n、m奇偶性不同时,方案数为0

                (证明:1、反证法:只看横行为偶数个奇数,只看竖行为奇数个奇数,但总和不应有变,矛盾

                              2、利用(n-1)*(m-1)的矩形来证明:在(n-1)*(m-1)表上均为1时,最后一行和最后一列奇偶性必然不同,而每将1个1变为0,两边的奇偶性都不会变为相同)

                 

               至于求2^(n-1)^(m-1)就没什么好讲的了,只要注意快速幂必须分两次求,否则(n-1)*(m-1)会爆long long

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 const int MOD=1000000007;
     5 
     6 long long n,m;
     7 int k;
     8 
     9 long long quick_pow(long long a,long long b)
    10 {
    11     long long base=a,res=1;
    12     while(b)
    13     {
    14         if(b%2) res=res*base%MOD;
    15         b>>=1;
    16         base=base*base%MOD;
    17     }
    18     
    19     return res;
    20 }
    21 
    22 int main()
    23 {
    24     cin >> n >> m >> k;
    25     
    26     if(k==-1 && (n%2!=m%2)) cout << 0;
    27     else cout << quick_pow(quick_pow(2,n-1),m-1);
    28     
    29     return 0;
    30 }
    Problem B

    Problem C:Marco and GCD Sequence

    这次中国选手出题就变成数学大赛了啊(雾

    给定有序集合S,S是由一个n个元素组成的序列中对于每个1 ≤ i ≤ j ≤ n计算出的gcd(ai, ai + 1, ..., aj)产生的,输出你构造的序列,如没有输出-1

    Analysis:又是一道思维题

                  我当时的想法是既然序列中的数肯定都属于S,且S中的最后一个数必选,那从后往前贪心选取不就行了。当时写起来便很虚,其实明显是有bug的,因为没有想到普遍构造

                  

                  对于这类构造题,首先可以想一想如果将条件变强,是否存在一种通解

                  对于本题而言,便猜想如果s1,s2,s3.......sn均为s1,即gcd(a1,a2....an)的倍数时,便可将序列构造为s1,s2,s1,s3,s1,s4,s1,s5.....s1,sn

                  那如果s1不是s1,s2,s3....sn的公约数呢?那便可证明不存在这样的序列,因为a1,a2....an的最大公约数便是s1。

                  对于此题,我只想到S中的最后一个数必然为an,却未想到S1必然是a1,a2,a3.....an的最大公约数

                  对这类序列区间求值后统一排序的题目首尾均是关键

                  其次,在发现线性算法无法很好解决时,考虑对特定情况下的构造,再进行推广

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 int m,dat[1005];
     5 
     6 int gcd(int a,int b)
     7 {
     8     if(a%b==0) return b;
     9     else return gcd(b,a%b);
    10 }
    11 
    12 int main()
    13 {
    14     cin >> m;
    15     for(int i=1;i<=m;i++) cin >> dat[i];
    16     
    17     if(m==1)
    18     {
    19         cout << 1 << endl << dat[1];
    20         return 0;
    21     }
    22     
    23     int all=dat[1];
    24     for(int i=2;i<=m;i++) all=gcd(all,dat[i]);
    25     
    26     if(all!=dat[1]) cout << -1;
    27     else
    28     {
    29         cout << 2*(m-1) << endl;
    30         for(int i=2;i<=m;i++) cout << dat[1] << " " << dat[i] << " ";
    31     }
    32     
    33     return 0;
    34 }
    Problem C

    Problem D:Ralph And His Tour in Binary Country

    给定一棵树,每次给1个点A,求到A的距离不超过H的点到A的距离和

    Analysis:这道题一开始以为要树剖,结果发现并不用

                  在每一颗子树下,可以预处理出子树下所有点到子树根节点的距离及前缀和,从而O(logn)求出该子树下到子树根节点距离不超过H-x的和

                  因此,我们可以从A节点开始,一层层向上遍历,找父亲,直到距离已超过H,每次求出当前子树下到子树根节点B距离不超过H-x(A到B的距离)的和

                  但这样明显会有一定的重复计算,因此每次我们找到父亲后,只能查找该父亲下的另一棵子树

                  这时实现时有一些小技巧:1、当数据在节点时可以递归建树时。但当数据在边上时,应当以边为线索进行建树

                                                             2、树上的遍历为保证不重复,只要每次记录上一次操作的last节点,保证不重复计算

                                                             3、找到边与点的关系,在本题中,第i个点到其父节点的边为Ei-1

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 typedef long long ll;
     5 const int MAXN=1000005;
     6 
     7 int n,m,len[MAXN],A,H,t[MAXN];
     8 vector<int> a[MAXN];
     9 vector<ll> pre[MAXN];
    10 
    11 inline int read() //OI优化标准模块,专防卡常 
    12 {
    13     char ch;int num,f=0;
    14     while(!isdigit(ch=getchar())) f|=(ch=='-');
    15     num=ch-'0';
    16     while(isdigit(ch=getchar())) num=num*10+ch-'0';
    17     return f?-num:num;
    18 }
    19 
    20 inline void write(ll x)
    21 {
    22     if(x<0) putchar('-'),x=-x;
    23     if(x>9) write(x/10);
    24     putchar(x%10+'0');
    25 }
    26 
    27 void merge(int x,int y)  //类似于归并排序的操作 
    28 {    
    29     int lx=a[x].size(),ly=a[y].size(),d=len[y-1];
    30     int k=0,p=0,q=0;
    31     
    32     while(p<lx && q<ly)
    33     {
    34         if(a[x][p]<a[y][q]+d) t[k++]=a[x][p],p++;
    35         else t[k++]=a[y][q]+d,q++;
    36     }
    37     
    38     while(p<lx) t[k++]=a[x][p],p++;
    39     while(q<ly) t[k++]=a[y][q]+d,q++;
    40     
    41     a[x].clear();
    42     for(int i=0;i<k;i++) a[x].push_back(t[i]);
    43 }
    44 
    45 ll cal(int node,ll tar)
    46 {
    47     ll ret=0;
    48     ll pos=lower_bound(a[node].begin(),a[node].end(),tar)-a[node].begin()-1;
    49     ret=tar*(pos+1)-pre[node][pos];
    50     
    51     return ret;
    52 }
    53 
    54 ll BinaryCount(int node)
    55 {
    56     ll res=0,t=H,last=node; //last保证不重复计算 
    57     res+=cal(node,t);t-=len[node-1];node>>=1; //先加上当前节点 
    58     while(node>0 && t>0)
    59     {
    60         res+=t; //加上当前的根节点 
    61         int lch=(node<<1),rch=((node<<1)|1); //处理当前根节点下的子节点 
    62         if(lch<=n && lch!=last && t-len[lch-1]>0) res+=cal(lch,t-len[lch-1]);
    63         if(rch<=n && rch!=last && t-len[rch-1]>0) res+=cal(rch,t-len[rch-1]);
    64         
    65         last=node;t-=len[node-1];node>>=1;
    66     }
    67     return res;
    68 }
    69 
    70 int main()
    71 {
    72     cin >> n >> m;
    73     for(int i=1;i<=n-1;i++) len[i]=read();
    74     for(int i=1;i<=n;i++) a[i].push_back(0);
    75     
    76     for(int i=n;i>1;i--) merge(i/2,i);  //以边为线索进行更新 
    77     
    78     for(int i=1;i<=n;i++) //对前缀和的预处理 
    79         for(int j=0;j<a[i].size();j++)
    80             if(!j) pre[i].push_back(a[i][j]);
    81             else pre[i].push_back(pre[i][j-1]+a[i][j]);
    82     
    83     for(int i=1;i<=m;i++)
    84     {
    85         cin >> A >> H;
    86         write(BinaryCount(A));cout << endl;
    87     }
    88     
    89     return 0;
    90 }
    Problem D

    Problem E:Ralph and Mushrooms

    Analysis:莫名其妙,E题成了道简单题

                  主要就是先Tarjan缩点,在每一个强联通分量里对数据进行特殊计算

                  接下来在DAG上求最长路

                  注意,此题的起点不一定入度为零,因此不能直接拓扑排序,应记忆化搜索

                  Tips:1、对(n-1)+(n-2)+(n-3)+(n-4)......(n-k)的计算是一个难点,标算的数学计算法还没看懂......

                          2、对于DAG的处理:不一定非要拓扑+DP,可以直接记忆化搜索,但要注意的是此时维护的是到终止节点的最大距离

                          3、Tarjan缩点后重新建图时,可以不用处理两个节点间的多条边

      1 #include <bits/stdc++.h>
      2 
      3 using namespace std;
      4 typedef pair<int,int> P;
      5 
      6 const int N=1000005;
      7 int n,m,st,low[N],dfn[N],time_point=0,col[N],cnt=0;
      8 bool vis[N],instack[N];
      9 long long res=0,tot[N],dp[N];
     10 
     11 inline int read()
     12 {
     13     char ch;
     14     while(!isdigit(ch=getchar()));
     15     int num=ch-'0';
     16     while(isdigit(ch=getchar())) num=num*10+ch-'0';
     17     return num;
     18 }
     19 
     20 struct ed
     21 {
     22     int x,y,w;
     23 }edge[N];
     24 
     25 vector<P> a[N];
     26 stack<int> s;
     27 
     28 void tarjan(int cur)  //Tarjan模板 
     29 {
     30     time_point++;
     31     dfn[cur]=low[cur]=time_point;
     32     vis[cur]=true;
     33     instack[cur]=true;
     34     s.push(cur);
     35     
     36     for(int i=0;i<a[cur].size();i++)
     37     {
     38         int u=a[cur][i].first;
     39         if(!vis[u])
     40         {
     41             tarjan(u);
     42             low[cur]=min(low[cur],low[u]);
     43         }
     44         else if(instack[u])
     45         {
     46             low[cur]=min(low[cur],low[u]);
     47         }
     48     }
     49     
     50     if(dfn[cur]==low[cur])
     51     {
     52         cnt++;int t;
     53         do
     54         {
     55             t=s.top();s.pop();
     56             instack[t]=false;
     57             col[t]=cnt;
     58         }while(t!=cur);
     59     }
     60 }
     61 
     62 long long cal(int t)  //标算的数学计算法 
     63 {
     64     long long k=(long long)(sqrtl((long double)t*2+0.25)-0.5);
     65     return (k*(6*t-(k+1)*(k+2)))/6+t;
     66 }
     67 
     68 void dfs(int node) //记忆化 
     69 {
     70     if(dp[node]) return;
     71     for(int i=0;i<a[node].size();i++)
     72     {
     73         int v=a[node][i].first;
     74         dfs(v);
     75         dp[node]=max(dp[node],dp[v]+a[node][i].second);  //维护到终止点的值 
     76     }
     77     dp[node]+=tot[node];  //在完结后才加上当前节点的值 
     78 }
     79 
     80 int main()
     81 {
     82     n=read();m=read();
     83     for(int i=1;i<=m;i++)
     84     {
     85         edge[i].x=read();edge[i].y=read();edge[i].w=read();
     86         a[edge[i].x].push_back(P(edge[i].y,edge[i].w));
     87     } 
     88     st=read();
     89     
     90     tarjan(st);
     91     
     92     for(int i=0;i<N;i++) a[i].clear();
     93     
     94     for(int i=1;i<=m;i++)  //对缩点后每一个点的数据更新 
     95     {
     96         int u=edge[i].x,v=edge[i].y;
     97         if(col[u]==col[v])
     98         {
     99             tot[col[u]]+=cal(edge[i].w);
    100         }
    101     }
    102     for(int i=1;i<=m;i++)
    103     {
    104         int u=edge[i].x,v=edge[i].y;  //这里可以不用考虑两个点间有多条边的情况 
    105         if(col[u]!=col[v])
    106         {
    107             a[col[u]].push_back(P(col[v],edge[i].w));
    108         }        
    109     }
    110     
    111     dfs(col[st]);
    112     
    113     cout << dp[col[st]];
    114     
    115     return 0;
    116 }
    Problem E
  • 相关阅读:
    提交按钮变灰
    解析spring启动加载dubbo过程
    shiro的SecurityUtis
    spring集成shiro登陆流程(下)
    spring集成shiro登陆流程(上)
    shiro的DelegatingFilterProxy怎么找到ShiroFilterFactoryBean
    sql多表关联
    android常用控件
    android控件之EditText
    浅议iOS网络数据解析
  • 原文地址:https://www.cnblogs.com/newera/p/7900570.html
Copyright © 2011-2022 走看看