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\n" , v.size());
31     for(int i=0  ; i<v.size() ; i++)
32         printf("%d %d\n" , 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\n" , (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\n" , 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\n" , (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\n" , (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\n" , ans);
84     return 0;
85 }

cf501E

时间: 2024-10-05 15:42:07

codeforces 501 C,D,E的相关文章

codeforces 501 B Misha and Changing Handles 【map】

题意:给出n个名字变化,问一个名字最后变成了什么名字 先用map顺着做的,后来不对, 发现别人是将变化后的那个名字当成键值来做的,最后输出的时候先输出second,再输出first 写一下样例就好理解了 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7

Codeforces Round #501 (Div. 3) F. Bracket Substring

题目链接 Codeforces Round #501 (Div. 3) F. Bracket Substring 题解 官方题解 http://codeforces.com/blog/entry/60949 ....看不懂 设dp[i][j][l]表示前i位,左括号-右括号=j,匹配到l了 状态转移,枚举下一个要填的括号,用next数组求状态的l,分别转移 代码 #include<bits/stdc++.h> using namespace std; const int maxn = 207;

Codeforces Round #501 (Div. 3) A Points in Segments

翻译 现在有一个数轴,上面会有\(M\)个点,标号为\(1\)到\(N\),现在给你在数轴上的条\(N\)线段的起始与终止的点,问哪几个点没有被这样线段覆盖,从小到大输出. 思路 签到题目.感觉几乎和一道题一样:校门外的树,撞题是很尴尬.思路差不多,即为开一个数组,全部赋值为\(0\),输入的线段的时候,将其起点与终点的全部的点赋值为\(1\),最后跑一下看看那些为\(0\)的点就完事了. Code #include<iostream> using namespace std; int boo

Codeforces Round #501 (Div. 3) B Obtaining the String

翻译 给你两个字符串\(s\)与\(t\),你每次可以交换字符串\(s\)种相邻两个字符,请你输出字符串\(s\)变成\(t\)的步骤(如果输出\(k\),代表交换了\(k\)与\(k+1\)),如果有多组解,随意输出一种即可. 思路 这道题一开始考虑复杂了,导致我发奋图强到\(11:40\)才\(A\)掉,我\(12:00\)必须睡觉因为明天有课\(www\). 实际不难,这题是\(SPJ\),我是这么想的:我们都知道任意\(1\)个字符可以通过交换相邻的两个字符来跑遍整个字符串. 进而可以得

Codeforces Round #501 (Div. 3) D Walking Between Houses

翻译 给你一条数轴(出题人很喜欢数轴啊),上面有排列着\(n\)个点,编号为\(1\)到\(n\)(你开始在\(1\)).每次你要从一个点移动到另一个点(可以重复).移动距离为两点坐标之差的绝对值,问你是否能否在\(k\)次机会内里一共移动\(s\)距离,并输出方案. 思路 第四题还是比较难的,赛后想了几分钟时才有头绪. 毫无疑问还是贪心(出题人很喜欢贪心啊)!那么我们分类讨论,先看不合法的情况 无论如何走你也走不到那个距离 无论如何走你也会走超那个距离 看来只要能走到那个距离除外都是不合法的,

Codeforces Round #249 (Div. 2)-D

这场的c实在不想做,sad. D: 标记一下每一个点8个方向不经过黑点最多能到达多少个黑点. 由题意可知,三角形都是等腰三角形,那么我们就枚举三角形的顶点. 对于每一个定点,有8个方向可以放三角形. 然后枚举8个方向,然后枚举腰的长度.然后判断是否可行. #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h>

[Codeforces]#179 div1-----295ABCDE

摘自我的github:https://github.com/Anoxxx The Solution Source: Codeforces Round #179 (Div. 1) VJudge链接: https://cn.vjudge.net/contest/167920#problem CodeForces链接: http://codeforces.com/problemset/problem/295 #A Greg and Array Time limit: 1500 ms Memory li

codeforces #285 div.2 C. Misha and Forest

题目链接:    http://codeforces.com/contest/501/problem/C 题意:    给出图的每个顶点的度数和相邻顶点的异或值的和,求出这个图的边的情况和边的数目: 分析:   找出度为一的点,其所对应的异或值即为与之相邻的顶点(<- ^ ->),再将与之相邻顶点的度数减一: **********代码: #include<iostream> #include<cstdio> #include<cstring> #includ

Codeforces 501C Misha and Forest(bfs)

题目链接:http://codeforces.com/problemset/problem/501/C 题意: 思路: 代码: #include <iostream> #include <stdio.h> #include <cstring> #include <queue> #include <set> #include <cmath> #include <time.h> #include <cstdlib>