zoukankan      html  css  js  c++  java
  • codeforces 501 C,D,E

    C题意:

    给定n个点(标号0~n-1)的度数(就是与其邻接的点的个数)和所有与它邻接的点标号的异或和,求满足这些条件的树的边应该是怎么连的,将边输出出来

    这里可以理解成拓扑排序的方式考虑,当i度数为1的时候,那么我们必然知道 i 肯定得与s[i]相连使其剩余的异或值为0

    所以建立一个队列不断将度数变为1的点放进来,得到边以后,不断更新点的度数和其对应的异或和的值

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 const int N = (1<<16);
     5 int n,degree[N] , s[N] , vis[N];
     6 #define pii pair<int,int>
     7 vector<pii> v;
     8 queue<int> q;
     9 int main()
    10 {
    11    // freopen("a.in" , "r" , stdin);
    12     scanf("%d" , &n);
    13     for(int i=0 ; i<n ; i++){
    14         scanf("%d%d" , &degree[i] , &s[i]);
    15         if(degree[i]==1) q.push(i);
    16     }
    17     while(!q.empty()){
    18         int u = q.front();
    19         q.pop();
    20   //  cout<<u<<" "<<degree[u]<<" "<<s[u]<<endl;
    21         if(degree[u]==0) continue;
    22         degree[u]--;
    23         if(!vis[s[u]]){
    24             degree[s[u]]-- , s[s[u]]^=u;
    25             if(degree[s[u]]==1) q.push(s[u]) , vis[s[u]]=1;
    26         }
    27         else degree[s[u]]--;
    28         v.push_back(make_pair(u , s[u]));
    29     }
    30     printf("%d
    " , v.size());
    31     for(int i=0  ; i<v.size() ; i++)
    32         printf("%d %d
    " , v[i].first , v[i].second);
    33     return 0;
    34 }
    cf 501C

    D题意:

    给定两个0~n-1的排列方式,像康托展开式那种方式计算对应排名(从0开始,也就是说排名可能是0~n!)

    将两种排列的排名相加后计算新的排名%n!后的对应的排列方式

    这里我们可以计算排列上每一位对应的名次,这个名次的含义是从最高位开始这个数在不曾出现的数字中的排名,这个排名可以用树状数组解决

    如序列 2 1 3 0 4 对应排名 2 1 2 0 0

    这里给定样例

    5

    2 1 3 0 4

    2 0 4 3 1

    可以看出来两个序列排名后是:

    2 1 1 0 0

    2 0 2 1 0

    相当于把每一位相加就说明得到新排名是符合总和相加所得排名的(不懂可以学一下康拓展开)

    就是4 1 3 1 0

    那这里对应每一位来说,都不应该出现超出当前位所能承受的排名就进位,比如这里3的位置最多排名应该为2,所以变成3%3=0,前面进位1+1=2

    新的排名是4 2 0 1 0

    根据新的排名倒过来算出每一位上的数字,我用的是线段树处理,当然应该有更好的办法,只是我比较弱~

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 const int N = 100005;
     5 int n , a[N] , cnt[N] , use[N] , use1[N];
     6 #define ll long long
     7 ll ans;
     8 int main()
     9 {
    10    // freopen("a.in" , "r" , stdin);
    11     scanf("%d" , &n);
    12     for(int i=1 ; i<=n ; i++){
    13         scanf("%d" , &a[i]);
    14         cnt[a[i]]++;
    15     }
    16 
    17     int odd = 0;
    18     for(int i=1 ; i<=n ; i++){
    19         if(cnt[i]&1) odd++;
    20     }
    21     if(((n&1)==0 && odd) || ((n&1)&&odd>1)){
    22         puts("0");
    23         return 0;
    24     }
    25     //从左右两端出发能匹配到的最大次数
    26     int maxMatch = 0;
    27     for(int i=1,j=n ; i<=j ; i++,j--){
    28         if(a[i]==a[j])maxMatch++;
    29         else break;
    30     }
    31     if(maxMatch==(n+1)/2){
    32         printf("%I64d
    " , (ll)n*(n+1)/2);
    33         return 0;
    34     }
    35     //从中间出发能匹配到的最大次数
    36     int maxMid = 0;
    37     for(int i=n/2 , j=(n+1)/2+1; i>0 ; i-- , j++){
    38         if(a[i]==a[j])maxMid++;
    39         else break;
    40     }
    41     ans = 0;
    42 
    43     //计算最右侧出发还存在值的最大位置
    44     int mxRg = 0;
    45     for(mxRg=1 ; mxRg<=n ; mxRg++){
    46         if(n&1 && mxRg==(n+1)/2 && cnt[a[mxRg]]&1) continue;
    47         else if(n&1 && mxRg==(n+1)/2) break;
    48         if(mxRg<=(n+1)/2){
    49             if(use1[a[mxRg]]+1<=cnt[a[mxRg]]/2) {use1[a[mxRg]]++;continue;}
    50             else break;
    51         }
    52         else {
    53             if(a[mxRg]==a[n-mxRg+1]) continue;
    54             else break;
    55         }
    56     }
    57     //计算找到如果没有回文重排点的右半边最小位置
    58     int last = n;
    59     int index=1;
    60     while(last>=index){
    61         if(n&1 && last==(n+1)/2 && cnt[a[last]]&1) {last--;continue;}
    62         else if(n&1 && last==(n+1)/2) break;
    63         if(last<=n/2){
    64             if(maxMid) last-=maxMid;
    65             break;
    66         }
    67         else {
    68             if(last>=n-index+2 && a[last]!=a[n-last+1]) break;
    69             if(use[a[last]]+1>cnt[a[last]]/2) break;
    70             else use[a[last]]++ , last--;
    71         }
    72     }
    73     if(last<index) last=index;
    74   //  cout<<"last: "<<last<<endl;
    75     /*----------------*/
    76     if(mxRg>n) mxRg=n;
    77     for(int index=1 ; index<=mxRg ; index++){
    78         int flag = index*n;
    79         int val = max(n-index+1 , n-maxMatch);
    80        // cout<<"first: "<<index<<" "<<last<<endl;
    81         if(val == n-index+1) ans+=n-(last<index?index:last)+1;
    82         else ans+=maxMatch+1;//前面如果整个序列是回文串就会直接输出答案,所以这里maxMatch不可能会超过一半
    83        // cout<<index<<" "<<ans<<endl;
    84     }
    85     printf("%I64d
    " , ans);
    86     return 0;
    87 }
    cf 501D

    E题题意:

    就是给定一个数字序列,任取一段区间重新排列,要是能找到一种排列方式使之后形成的整个序列是个回文序列,就说明取的这段区间是个合法区间

    问有多少个合法区间

    这题目开始考虑区间[l,r]合法,那么说明取[l , r'] r'>r均合法

    那么只要枚举每一位l,快速找到最小的r即可用ans+=n-r+1就能算出来

    将序列本身是回文的和序列永远无法变成回文特判,相信这个大家都认为是很简单的

    我们找到一段从左端开始,和最右端开始的回文匹配得到能匹配到的最大值

     1 //从左右两端出发能匹配到的最大次数
     2     int maxMatch = 0;
     3     for(int i=1,j=n ; i<=j ; i++,j--){
     4         if(a[i]==a[j])maxMatch++;
     5         else break;
     6     }
     7     if(maxMatch==(n+1)/2){
     8         printf("%I64d
    " , (ll)n*(n+1)/2);
     9         return 0;
    10     }

    我们每次得到一个新的l,如果这个l之前的串都已经能被回文匹配,说明那一段无论如何都取的到,那只要知道我任意匹配所能走到r的最小位置

    如果没有满足全部回文匹配,那么说明最多只能从回文断开的地方取区间,总大小正好是maxMatch+1;

    比如r可以从当前位置n走,在走过一半前只要判断取到的数是否超过原个数的一半即可,没超过说明合法

    如果走过了一半 说明前面取到的数正好是一半,只要当前能够跟对应的位置匹配就可以取不取都无所谓了

    这里我觉得还要考虑一个正好到奇数个数的中点的特判,保证那个位置是唯一出现的奇数个数的数即可

    根据上述方法,再计算一个可枚举的l范围从1到k停止,也是根据上方所述一步步走:

     1 //计算最右侧出发还存在值的最大位置
     2     int mxRg = 0;
     3     for(mxRg=1 ; mxRg<=n ; mxRg++){
     4         if(n&1 && mxRg==(n+1)/2 && cnt[a[mxRg]]&1) continue;
     5         else if(n&1 && mxRg==(n+1)/2) break;
     6         if(mxRg<=(n+1)/2){
     7             if(use1[a[mxRg]]+1<=cnt[a[mxRg]]/2) {use1[a[mxRg]]++;continue;}
     8             else break;
     9         }
    10         else {
    11             if(a[mxRg]==a[n-mxRg+1]) continue;
    12             else break;
    13         }
    14     }
    15     if(mxRg>n) mxRg=n;

    得到那个最小的位置last后结果就很容易算了

    1 for(int index=1 ; index<=mxRg ; index++){
    2         int flag = index*n;
    3         int val = max(n-index+1 , n-maxMatch);
    4        // cout<<"first: "<<index<<" "<<last<<endl;
    5         if(val == n-index+1) ans+=n-(last<index?index:last)+1;
    6         else ans+=maxMatch+1;//前面如果整个序列是回文串就会直接输出答案,所以这里maxMatch不可能会超过一半
    7        // cout<<index<<" "<<ans<<endl;
    8     }
     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 const int N = 100005;
     5 int n , a[N] , cnt[N] , use[N] , use1[N];
     6 #define ll long long
     7 ll ans;
     8 int main()
     9 {
    10    // freopen("a.in" , "r" , stdin);
    11     scanf("%d" , &n);
    12     int time =0;
    13     for(int i=1 ; i<=n ; i++){
    14         scanf("%d" , &a[i]);
    15         if(cnt[a[i]]==0) time++;
    16         cnt[a[i]]++;
    17     }
    18     if(time==1){
    19         printf("%I64d
    " , (ll)n*(n+1)/2);
    20         return 0;
    21     }
    22     int odd = 0;
    23     for(int i=1 ; i<=n ; i++){
    24         if(cnt[i]&1) odd++;
    25     }
    26     if(((n&1)==0 && odd) || ((n&1)&&odd>1)){
    27         puts("0");
    28         return 0;
    29     }
    30     //从左右两端出发能匹配到的最大次数
    31     int maxMatch = 0;
    32     for(int i=1,j=n ; i<=j ; i++,j--){
    33         if(a[i]==a[j])maxMatch++;
    34         else break;
    35     }
    36     //从中间出发能匹配到的最大次数
    37     int maxMid = 0;
    38     for(int i=n/2 , j=(n+1)/2+1; i>0 ; i-- , j++){
    39         if(a[i]==a[j])maxMid++;
    40         else break;
    41     }
    42     ans = 0;
    43     int last = n;
    44 
    45     //计算最右侧出发还存在值的最大位置
    46     int mxRg = 0;
    47     for(mxRg=1 ; mxRg<=n ; mxRg++){
    48         if(n&1 && mxRg==(n+1)/2 && cnt[a[mxRg]]&1) continue;
    49         else if(n&1 && mxRg==(n+1)/2) break;
    50         if(mxRg<=(n+1)/2){
    51             if(use1[a[mxRg]]+1<=cnt[a[mxRg]]/2) {use1[a[mxRg]]++;continue;}
    52             else break;
    53         }
    54         else {
    55             if(a[mxRg]==a[n-mxRg+1]) continue;
    56             else break;
    57         }
    58     }
    59     if(mxRg>n) mxRg=n;
    60     for(int index=1 ; index<=mxRg ; index++){
    61         int flag = index*n;
    62         last = max(n-index+1 , n-maxMatch);
    63        // cout<<"first: "<<index<<" "<<last<<endl;
    64         if(last<n && last == n-index+1) cnt[a[last+1]] -= 2;
    65         while(last>=index){
    66             if(n&1 && last==(n+1)/2 && cnt[a[last]]&1) {last--;continue;}
    67             else if(n&1 && last==(n+1)/2) break;
    68             if(last<=n/2){
    69                 if(maxMid) last-=maxMid;
    70                 break;
    71             }
    72             else {
    73                 if(use[a[last]]<flag) use[a[last]]=flag;
    74                 if(last>=n-index+2 && a[last]!=a[n-last+1]) break;
    75                 if(use[a[last]]+1-flag>cnt[a[last]]/2) break;
    76                 else use[a[last]]++ , last--;
    77             }
    78         }
    79         if(last<index) last=index;
    80           // cout<<index<<": "<<last<<" "<<maxMid<<endl;
    81         ans += n-last+1;
    82     }
    83     printf("%I64d
    " , ans);
    84     return 0;
    85 }
    cf501E
  • 相关阅读:
    ELK+Kafka+Beats实现海量日志收集平台(三)
    ELK+Kafka+Beats实现海量日志收集平台(二)
    ELK+Kafka+Beats实现海量日志收集平台(一)
    Linux 上安装logstash-6.6.0及配置kafka输入
    Linux 上安装filebeat-6.6.0
    SpringBoot与Kafka整合实现简单分布式消息队列
    logstash-6.6.0.tar.gz、filebeat-6.6.0-linux-x86_64.tar.gz 下载百度云
    Centos 7.X 上安装Kafka及一些常用命令
    Linux MariaDB 10.4.8 官网地址下载及百度云
    [Errno 28] No space left on device ERROR: Could not install packages due to an EnvironmentError
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/5163031.html
Copyright © 2011-2022 走看看