新生训练class2_暴力

  • A New Year and Counting Cards
    题意:每张牌两面分别是字母和数字。定义一个规则:如果一张牌的字母是元音字母那么另一面一定对照着偶数。给你n个牌的一面,问你最少翻多少张牌最终判断这些牌的对照是正确的。
    解题思路:直接统计有多少个是元音字母或者是奇数。
    根据以上规则,1.元音字母只能对应偶数。2.偶数可以对应所有字母,而奇数只能对应非元音字母。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include<ctype.h>//isdigit(),isalpha()函数的头文件 
using namespace std;

bool isyuanyin(char s){
	if(s=='a'||s=='e'||s=='i'||s=='o'||s=='u')return true;
	else return false;
}
int main(){
	string s;
	int cnt;
	int tmp1;
	char tmp2;
	while(getline(cin,s)){
		cnt=0;
		for(int i=0;i<s.length();i++){
			if(isdigit(s[i])){//判断是否为数字 
				tmp1=int(s[i]);
				if(tmp1&1==1)cnt++;	//判断是否为奇数 
			}
			else if(isalpha(s[i])){//判断是否为字母 
				tmp2=s[i];
				if(isyuanyin(tmp2)){
					cnt++;
				}
				
			}
		}
		cout<<cnt<<endl;
		//要清空 s吗,不需要每次都清空s,在getline的时候就会把s清空的 
	}
} 

解法二(统计元音和奇数个数):
#include<bits/stdc++.h>
using namespace std;
string c="aeiou13579";
int main()
{
    string s;
    cin>>s;
    int ans=0;
    for(int i=0;s[i];i++)
        for(int j=0;c[j];j++)
            if(s[i]==c[j])
                ans++;
    cout<<ans;
    return 0;
}
  • B New Year and Buggy Bot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <iostream>
using namespace std;
int a[55][55];
// 1上 2 下 3 左 4 右
int s1,s2,e1,e2;
int ans;
int n,m;
string abc;
int main()
{

    mem(a,0);
    cin>>n>>m;
    string str;
    ans=0;
    for(int i=0;i<n;i++)
    {
        cin>>str;
        for(int j=0;j<str.length();j++)
        {
            if(str[j]=='.')
                a[i][j]=1;
            else if(str[j]=='#')
                a[i][j]=2;
            else if(str[j]=='S')
            {
                s1=i,s2=j;
                //a[i][j]=1;
            }
            else if(str[j]=='E')
            {
                //a[i][j]=1;
                e1=i;e2=j;
            }
        }
    }
    //cout<<s1<<" "<<s2<<" "<<e1<<" "<<e2<<endl;
    cin>>abc;
    for(int x=1;x<=4;x++)
    {
        for(int y=1;y<=4;y++)
        {
            for(int z=1;z<=4;z++)
            {
                for(int d=1;d<=4;d++)
                {
                    if(x!=y&&x!=z&&x!=d&&y!=z&&y!=d&&z!=d)
                    {
                       // cout<<x<<y<<z<<d<<endl;
                        int i,j,c;
                        for(i=s1,j=s2,c=0;c<abc.length();c++)
                        {
                            if(abc[c]=='0')
                            {
                                if(x==1)//shang
                                    i-=1;
                                else if(x==2)//xia
                                    i+=1;
                                else if(x==3)//zuo
                                    j-=1;
                                else if(x==4)//you
                                    j+=1;
                            }
                            else if(abc[c]=='1')
                            {
                                if(y==1)
                                    i-=1;
                                else if(y==2)
                                    i+=1;
                                else if(y==3)
                                    j-=1;
                                else if(y==4)
                                    j+=1;
                            }
                            else if(abc[c]=='2')
                            {
                                if(z==1)
                                    i-=1;
                                else if(z==2)
                                    i+=1;
                                else if(z==3)
                                    j-=1;
                                else if(z==4)
                                    j+=1;
                            }
                            else if(abc[c]=='3')
                            {
                                if(d==1)
                                    i-=1;
                                else if(d==2)
                                    i+=1;
                                else if(d==3)
                                    j-=1;
                                else if(d==4)
                                    j+=1;
                            }
                            if(a[i][j]==2)
                                break;
                            if(i<0||i>=n||j<0||j>=m)
                                break;


                                if(i==e1&&j==e2)
                                {
                                   // cout<<x<<" "<<y<<" "<<z<<" "<<d<<endl;
                                    ans++;
                                    break;
                                }


                        }
                    }
                }
            }
        }
    }

    cout<<ans<<endl;
    return 0;
}





/*
解法二:
#include<bits/stdc++.h>
using namespace std;
string s[51];
int a[6],sx,sy;
int main()
{
//4!=24种  最大24种  24种排列,就是a4 4 
//a[0],a[1],a[2],a[3]分别带别上下左右,来全排列找到结果 
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
        cin>>s[i];
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        if(s[i][j]=='S')sx=i,sy=j;
    string c;
    cin>>c;
    for(int i=0;i<4;i++)
        a[i]=i+48;//把a[i]转换为与字符型相对应的a[i] 
    int ans=0;
    do
    {
        int f=0;
        int tx=sx,ty=sy;
        for(int i=0;c[i];i++)
        {
            if(c[i]==a[0])
                tx-=1;
            else if(c[i]==a[1])
                tx+=1;
            else if(c[i]==a[2])
                ty-=1;
            else ty+=1;
            if(tx<0||ty<0||tx>=n||ty>=m)
                break;
            if(s[tx][ty]=='#')break;
            if(s[tx][ty]=='E'){f=1;break;}
        }
        ans+=f;
    }while(next_permutation(a,a+4));
    cout<<ans;
    return 0;
}*/
  • C - Water The Garden

题意:给你编号1~n的花园,花园从左到右排成一排。其中有k个花园有水龙头,当水龙头所在的花园浇满后,会流向左右相邻花园,现在同时打开所有水龙头,问你多长时间后n个花园能被浇满。

解法一:思路:这个题目很有意思,只用把握一点就好:所有水龙头同时开启,所有水流同时流动。所以先看边界情况,从编号最小的水龙头到最左端,这一段只有一个方向的水流,最右端也是同理,这两个边界都只有一个方向的水流,而且它们同时流动,所以边界所花时间是两者中较大的:ans=max(a[1]-1,n-a[k])。然后是中间部分,一定是来自两个方向的水流,此时要看间隔的个数x,如果x是奇数,那么时间就是x/2+1,偶数就是x/2,这个自己想一下就能想明白,而且所有中间间隔的双向水流都是同时流动,所以依然是取最大值即可,而不是累加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
解法一:
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll a[110],n,k;
    int main()
    {
        ll t;
        scanf("%lld",&t);
        while(t--)
        {
            scanf("%lld%lld",&n,&k);
            for(ll i=1;i<=k;i++)scanf("%lld",&a[i]);
            ll ans=max(a[1]-1,n-a[k]);         //处理边界的单向水流
            for(ll i=2;i<=k;i++)
            {
                ll tmp=a[i]-a[i-1]-1;
                if(tmp&1)tmp=tmp/2+1;
                else tmp/=2;
                ans=max(ans,tmp);            //更新中间的双向水流
            }
            printf("%lld\n",ans+1);
        }
        return 0;
    }


解法二:
#include <bits/stdc++.h>
using namespace std;
bool check(bool flag[], int n) {
	for (int i = 1; i <= n; i++) {
		if (flag[i] == false) return true;
	}
	return false;
}
int main() {
	int cas;
	cin >> cas;
	for (int i = 1; i <= cas; i++) {
		int n, k;
		cin >> n >> k;
		int val[k];
		for (int j = 0; j < k; j++) {
			cin >> val[j];
		}
		//1-n
		bool flag[n+1];//对应1-n
		for (int j = 1; j <= n; j++) flag[j] = false;
		for (int j = 0; j < k; j++) {
			flag[val[j]] = true;//标记已填满	
		}
		int cnt = 1;
		while (check(flag, n)) {
			//对每次的进行遍历,每次都遍历val数组中的每个元素
			int change[n];
			int count = 0;
			for (int j = 1; j <= n; j++) {
				if (flag[j] == true) {
					int left = j-1;//
					int right = j+1;
					if (left >= 1 && flag[left] == false) {
						// flag[left] = true;
						change[count++] = left;
					} 
					if (right <= n && flag[right] == false) {
						// flag[right] = true;
						change[count++] = right;
					}
				}
			}
			for (int j = 0; j < count; j++) flag[change[j]] = true;
			cnt++;
		}
		cout << cnt << endl;
	}
	return 0;
}
  • D - Tea Queue
    题意: 已知n个人来接水,每秒可以接一个人的水,给出每个人的到达接水地点的时间,和走的时间,如果同一时间来的,先来的可以接到水,如果可以接到水,记录下这个人的接水时间,如果接不到,记录为0

    分析: 我们可以枚举时间轴,当当前时间大于这个人走的时间,则这人的最后记录为0,否则记录答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>

using namespace std;


const int maxn = 1e5 + 10;

bool vis[maxn];
int l[maxn];
int r[maxn];
int res[maxn];

int main(){
    int T;cin>>T;
    while(T--) {
        memset(vis,0,sizeof vis);
        int n;cin>>n;
        for(int i = 0;i < n;i++) cin>>l[i]>>r[i];
        for(int i = 1;i <= 5100;i++) {
            bool flg = false;
            for(int j = 0;j < n;j++) {
                if(!vis[j]) {
                    flg = true;break;
                }
            }
            if(!flg) break;
            for(int j = 0;j < n;j++) {
                if(vis[j]) continue;
                if(i > r[j]) {
                    vis[j] = true;
                    res[j] = 0;
                }
                if(l[j] <= i && r[j] >= i) {
                    vis[j] = true;
                    res[j] = i;
                    break;
                }
            }
        }
        for(int i = 0;i < n;i++) {
            printf("%d ",res[i]);
        }cout<<endl;
    }
    return 0;
}
  • E-A Compatible Pair
    就是两个数组,其中前一个数组中去掉一个最大之后,与后一个数组相乘,求这个相乘的最大值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define IO ios::sync_with_stdio(false);cin.tie(0);
#define INF 1e18
typedef long long ll;
using namespace std;
ll n, m, a[110], b[110];
ll maxm;
int main()
{
	while(cin >> n >> m){
		for(int i = 0; i < n; i++){
			cin >> a[i];
		}
		for(int i = 0; i < m; i++){
			cin >> b[i];
		}
		sort(a,a+n);
		sort(b,b+m);
		maxm=max(a[n-2]*b[m-1],a[1]*b[0]);
		//为什么最大值不是排序完了之后这两个的值呐,因为最大值有可能如下面那种解法 
		
		cout << maxm << endl;
	}
	return 0;
}

/*解法一: 
#include<bits/stdc++.h>

using namespace std;

#define ll long long
const ll INF_LL = 9223372036854775807LL;

ll a[maxn];
ll b[maxn];

int main(){
    ios_base::sync_with_stdio(0);
    int n,m;cin>>n>>m;
    for(int i = 0;i < n;i++) cin>>a[i];
    for(int i = 0;i < m;i++) cin>>b[i];
    sort(a,a+n);
    sort(b,b+m);
    ll a1 = a[0],a2 = a[1],a3 = a[n - 2],a4 = a[n - 1];
    ll b1 = b[0],b2 = b[1],b3 = b[m - 2],b4 = b[m - 1];
    ll res = -INF_LL;
    if(n > 2) {
        res = max(res,a2*b1);
        res = max(res,a2*b4);
        res = max(res,a3*b1);
        res = max(res,a3*b4);
        res = max(res,min(a[n - 1]*b[m - 1],a[0]*b[0]));
		//就有可能少了这种情况,所以只比较那两种结果会漏掉 
        res = max(res,min(a[n - 1]*b[0],a[0]*b[m - 1]));
    } else {
        res = max(res,a2*b1);
        res = max(res,a3*b4);
    }
    cout<<res<<endl;

    return 0;
}
*/ 

/*解法二: 
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define IO ios::sync_with_stdio(false);cin.tie(0);
#define INF 1e18
typedef long long ll;
using namespace std;
ll n, m, a[110], b[110];
int main()
{
	while(cin >> n >> m){
		for(int i = 0; i < n; i++){
			cin >> a[i];
		}
		for(int i = 0; i < m; i++){
			cin >> b[i];
		}
		ll maxm = -INF, loc;
		for(int i = 0; i < n; i++){
			for(int j = 0; j < m; j++){
				if(a[i]*b[j] > maxm){
					maxm = a[i]*b[j];
					loc=i;//记录最大值的下标,然后再一次遍历求最大值 
				} 
			}
		}
		maxm=-INF;//这样写代表maxn为负无穷小,这样就不会在比较时把更小的数给漏掉了 
		for(int i = 0; i < n; i++){
			for(int j = 0; j < m; j++){
				if(i == loc)
					continue;
				if(a[i]*b[j] >maxm){
					maxm = a[i]*b[j];
				}
			}
		} 
		cout << maxm << endl;
	}
	return 0;
}
*/

/*解法三: 
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
    int n,m;LL a[55],b[55],c[55];
    cin>>n>>m;
    for(int i=0;i<n;++i)cin>>a[i];
    for(int i=0;i<m;++i)cin>>b[i];
    for(int i=0;i<n;++i){
        c[i]=a[i]*b[0];
        for(int j=1;j<m;++j)
            c[i]=max(c[i],a[i]*b[j]);
			//其实这个把那个c[i]的值存放 b[0]到b[m-1],b这一整个数组的最大值
			// ????那有可能第二大的值是从a[i]*b[j]  里面运算的时候出现了呐 
			//,不会的因为a[i]取得的最大值要把此时的a[i]去掉,所以才会有 最大值,第二大,,,,第n*m-n大的值只能从c[]中取得
    }
    sort(c,c+n);cout<<c[n-2]<<endl;
    return 0;
}
*/
  • F-A Prosperous Lot
    题意: 0 4 6 9有一个环,8有两个环,其他没有环。求出一个数字,上面数位的环和答案为给定的x。如果这个数字超过1e18,输出-1.
    思路: 很明显,贪8就可以了。到了37,就超过1e18了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int n;scanf("%d",&n);
    if(n > 36)printf("-1\n");
    else if(n % 2 == 0)
    {
        for(int i = 1;i <= n / 2;i++)
            printf("%d",8);
    }
    else
    {
        printf("4");
        for(int i = 1;i <= n / 2;i++)
            printf("%d",8);
    }
    return 0;
}
  • G Subsequence
    给定长度n的数列整数,以及整数s,求出总和不小于S的连续子序列的长度的最小值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
解法一(不利用尺取法,利用lower_bound():
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include<cstdio>
    using namespace std;
    int n,ss;
    int a[100005],s[100005];
    int main(){
        int t;
        cin>>t;
        while(t--){
            memset(a,0,sizeof(a));
            memset(s,0,sizeof(s));
            int kk=100005;
            cin>>n>>ss;
            for(int i=1;i<=n;i++){
                cin>>a[i];
                s[i]=s[i-1]+a[i];
            }
            if(s[n]<ss)
                cout<<"0"<<endl;
            else{
                for(int i=1;s[i]+ss<=s[n];i++){
                    int k=lower_bound(s+i,s+n+1,s[i]+ss)-s;
                    kk=min(kk,k-i);
                }
                cout<<kk<<endl;
            }
        }
        return 0;
    }
    
    
 其中lower_bound该函数的用法:
 在从小到大的排序数组中,

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

在从大到小的排序数组中,重载lower_bound()和upper_bound()

lower_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

解法二:
因为枚举满足条件的区间,要枚举起点和终点,所以时间复杂度是o(n^3),所以考虑只枚举起点或者终点,即要去掉最后一个循环,所以:我们试试只枚举终点。对于终点j,我们的目标是要找到一个让Bj-Bi-1≥S,且i尽量大(i越大,序列长度j-i+1就越小)的i值,也就是找一个让Bi-1≤Bj-S最大的i。考虑图1-29所示的序列。(前缀和技巧,数组B就是存储前几项的和)

upload successful
当j=5时,B5=12,因此目标是找一个Bi-1≤12-7=5的最大i。注意到B是递增的(别忘了,本题中所有Ai均为整数),所以可以用二分查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
 
#define N 100001
 
int A[N];
int B[N];
 
//二分查找最接近target但不大于target
int BinarySerach(int target,int R){
    int L = 0;
    int mid = 0;
    while(L < R){
        mid = L + (R - L) / 2;
        if(B[mid] > target){
            R = mid;
        }
        else{
            L = mid + 1;
        }
    }
    return L;
}
 
int main(){
    int n,s,i,j;
    while(scanf("%d %d",&n,&s) != EOF){
        int minLen = n+1;
        B[0] = 0;
        for(i = 1;i <= n;i++){
            scanf("%d",&A[i]);
            //序列前缀和
            B[i] = B[i-1] + A[i];
        }
        for(j = 1;j <= n;j++){
            int target = B[j] - s;
            //二分查找
            int index = BinarySerach(target,j-1);
            if(index > 0){
                minLen = min(minLen,j-index+1);
            }
        }
        //没有满足条件的序列
        if(minLen == n+1){
            minLen = 0;
        }
        cout<<minLen<<endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
解法三(尺取法求前多少项的和,就是l和r两个指针,找到一个符合条件的区间,l++,子区间和减左边的第一个元素,然后在向后比较,这样把整个区间都走一遍,就能得到最重的结果。):
#include<iostream>
using namespace std;
#define min(a,b) a>b?b:a;
#define MAXN 100010
#define INF 0xfffffff
int a[MAXN];
int main()
{
	int n,N,S,l,r,len;
	long long sum  = 0;
	cin >>n;
	while(n--){
		cin>>N>>S;
		for(int i=0;i<N;i++){
			cin>>a[i];
		}
		l = r = 0;//初始化 
		sum = 0;
		len = INF;
		while(1){
			while(r<N && sum<S){ 
				sum+=a[r];
				r++;
			}//此时的sum是区间[l,r)上的和 
			if(sum<S){//如果说 sum<S但是r>=N了 结束循环 
				break;
			} 
			len = min(len,r-l);//更新长度 取较小的 
			sum -= a[l];//看看减去左边一个元素 还满不满足sum>=S 
			l++; //l右移一个 
		}
		if(len == INF){//如果说len没有变化 说明所有的数加起来<S 
			cout<<"0"<<endl; 
		}
		else{
			cout<<len<<endl;
		}
	}
	return 0;
}

变式:最大子列和问题
给定N个整数的序列 a1,a2,,,,,, an 求 求做大子列和,如果子列和为负数,那么结果为0
    #include<iostream>
    using namespace std;
    int a[100100];
    int main()
    {
    	int maxsum,sum,N;
    	maxsum = sum = 0;
    	cin>>N;
    	for(int i=0;i<N;i++){
    		cin>>a[i];
    	}//先读入数据 
    	for(int i=0;i<N;i++){
    		sum += a[i];
    		if(sum > maxsum){
    			maxsum = sum;
    		}
    		else if(sum <0 ){
    			sum = 0;
    		}
    	}
    	cout<<maxsum<<endl;
    	return 0;
    }
  • 本题的变题:编程之美求子数组最大值:https://blog.csdn.net/sunnyyoona/article/details/26288943

  • CodeForces - 908C New Year and Curling(圆与圆之间的关系的问题)
    题意: 已知一些圆在(xi,10100)处,然后依次落下,下落时只要碰到某个圆,就会停止,求这些圆的最后的y值,也即是水平高度
    分析: 首先看了下n的范围很小,可以到O(n2)
    的复杂度,直接暴力枚举,可以画图分析下,见下图

upload successful
可以看到高度h = y1 + sqrt(2r*2r - (x2-x1)(x2-x1));枚举个最高的高度即可,因为 一旦碰到就不会落下了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>

using namespace std;

int h[1500];
double res[1500];
int abs(int x,int y) {if(x - y > 0) return x - y;return y - x;}
int main(){
    ios_base::sync_with_stdio(0);
    int n;
    double r;
    cin>>n>>r;
    for(int i = 0;i < n;i++) {
        cin>>h[i];
        double rs = r;
        for(int j = 0;j < i;j++) {
            int dx = abs(h[i],h[j]);
            if(dx <= 2*r) {
                rs = max(rs,res[j] + sqrt(4*r*r - dx*dx));
            }
        }
        res[i] = rs;
    }
    for(int i = 0;i < n;i++) printf("%.10f ",res[i]);
    return 0;
}
  • H-Jessica’s Reading Problem
    题目大意:

XXX要准备考试,书总共有P页,第i页恰好有一个知识点ai,书中的同一个知识点可能会被多次提到,所以他希望看其中连续的一些页的书来把所有的知识点都给看完。。

题目分析:

其实页数可以看作连续的序列,然后就是要求出一个子序列,子序列的要求的包涵所有知识点都有的页数,且子序列的长度要最短。

首先我们并不知道总共有多少个知识点,这是个先要记录的。

不断的把序列中的元素加入到子序列中,知识点的数目会不断的增加,当知识点是数目满了之后,把子序列前面的元素开始剔除,再看看现在的子序列还是否满足这个条件。满足继续删除子序列前面的元素,否则的话把后面的元素加入到子序列中,然后按上述步骤继续执行。

注意中间过程知识点数的增加或减少的维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
尺取法求前多少项出现的次数(与前面那种求前多少项的和区别开来):
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
typedef long long LL;
const int N=100000+999;
int n,m;
int num[N];
//尺取法
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(num,0,sizeof(num));
        set<int> all; //利用集合元素的单一性
        map<int,int> cot;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&num[i]);
            all.insert(num[i]);
        }
        int res=n;
        m=all.size(); //算出种类
        int s=0,e=0,sum=0;
        while(1)
        {
            while(e<n && sum<m)
            {
                if(cot[ num[e++] ]++ == 0) //如果这个种类未出现 则sum++
                    sum++;
            }
            if(sum<m) break;//这一句是为了让最后e=n之后,能跳出外层的while循环。
            res=min(res,e-s);
            if(--cot[ num[s++] ] == 0) //如果这个种类-1后为0 则sum-1
                sum--;
        }
        printf("%d\n",res);
    }
    return 0;
}
  • I Graveyard Design
    求一组连续整数的平方和的问题,可以用尺取法模拟一个符合要去的区间的左边和右边,找到符合要求的区间就记录下来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL __int64
/*
一般来说,64位整型的定义方式有long long和__int64两种(VC还支持_int64),
而输出到标准输出
方式有printf(“%lld”,a),printf(“%I64d”,a),和cout << a三种方式。
*/
int num[2000],left[2000],right[2000];
 //对于数组,若下标大于1e7,就应该放到数组外面声明才可以
void solve(LL n)
{
	int i,j;
	LL m=(LL)sqrt(n*1.0);//因为sqrt只能对double类型运算 
	LL sum=0,l=1,r=0;
	int cnt=0;
	while(1)
	{
		while(sum<n)
		{
			r++;
			sum+=r*r;
		}
		if(r>m)
			break;
			//这一步是为了让当r>m的时候,已经不会有连续整数的平方和大于m了,
			//所以要跳出外层的while循环 
		if(sum==n)
		{
			num[cnt]=r-l+1;
			left[cnt]=l;
			right[cnt]=r;
			cnt++;
		}
		sum-=(l*l);
		l++;
	}
	printf("%d\n",cnt);
	for(i=0;i<cnt;++i)
	{
		printf("%d ",num[i]);
		for(j=left[i];j<right[i];++j)
			printf("%d ",j);
		printf("%d\n",right[i]);
	}
} 
 
int main()
{
	LL n;
	while(scanf("%I64d",&n)!=EOF)
		solve(n);
	return 0; 
}
  • J-骗分导论–ODT珂朵莉树
  • 讲义

  • 1.区间求和:
    前缀和,o(1)时间内求出前n项的和,或者某个区间的和(两个区间头减尾)
    前缀和
    前缀和S[i]=A[1]+A[2]+……+A[i];
    可以用A[i]=S[i]-S[i-1]来还原;
    还有二维的前缀和S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + A[i][j];
    这个公式可以用容斥原理推得。

  • 2.区间修改:
    单点查询(差分法)

    差分就是将数列中的每一项分别与前一项数做差,例如:
    一个序列     1 2 5, 4 7 3,  差分后得到1 1 3, -1 3 -4, -3
    上面整个序列+2后: 3 4 7, 6 9 5        3 1 3, -1 3 -4, -5
    // 只有整个区间的首和尾不同,其他的差分相同 ,即将首+2,(尾+1)的位置-2
    这里注意得到的差分序列第一个数和原来的第一个数一样(相当于第一个数减0)
    差分序列最后比原序列多一个数(相当于0减最后一个数)
    性质:
    1、差分序列求前缀和可得原序列
    2、将原序列区间[L,R]中的元素全部+x,可以转化操作为差分序列L处+x,R+1处-x
    3、按照性质2得到,每次修改原序列一个区间+x,那么每次差分序列修改相应的位置 增加的和减少的相同
    tips:前缀和与差分是一对逆运算。差分操作有助于把原序列上的“区间操作”
    转化为差分序列上的“单点操作”,在树上有着独特的应用。

    1. 尺取法:
      头尾双指针

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
         形式为:  
      while(1){   
      	while(表达式1){  
      		if(条件1)语句1 
      	}
      	if(条件2){break;} 
      	语句2 
      	if(条件3)语句3 
      }
      

      其中:表达式1为边界判断条件,目的是让在语句1中移动左指针,直到移动到符合要求的那个地方;条件2是边界判断(如:个数超过要求的总个数,sum超过要求的总和,其实就是左指针从头跑到尾了,可以结束外层while了;语句2可以执行一些操作,如对结果的处理,如res=min(res,e-s);条件3为边界判断,是左指针右移后对结果的处理,
      如:
      if(–cot[ num[s++] ] == 0)   //如果这个种类-1后为0 则sum-1
      sum–;        //这样的话,就是集合中少了那一种元素,对结果产生了影响, 就应该对结果进行相应的处理;
      其中语句2和语句3试情况而定,可调换顺序或者没有该语句;

  • 4.要想区间求和并且实现单点查询 分为根下n块,每一块就有根下n个数
    5 4 1, 8 10 1, 8 7 6, 5 4 => 求区间第三个到第十个的和
    10 19 21 9 =>那么结果就是1+19+21+5
    1. 5 4 6, 8 10 1, 8 7 6,10 4
      重新分块后: 5 4 6, 8 1 2, 3 4 10, 1 8 7, 6 10 4
      =>若果在第四个位置插入4个数,那么可以用再分块(当插入的数大于
      根下12为3,所以先3个一组,)的方法来维系块的性质
    1. [省选题](https://www.luogu.org/problem/P4799)
      100 1500, 500 500 1000
      =>分为两块进行搜索,在第一个块中进行搜索,在第二个快中进行搜索,再拼凑起来,对结果是没有影响的
      折半搜索,分治
      参考blog:https://www.cnblogs.com/ZAGER/p/9827160.html

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      
      	其中:1-4组数据N≤20,爆搜就可以解决。
      		inline void dfs(R ll dep,R ll sum){
      		 if(sum>m)return;//只要sum大于总的m就可以结束本次递归 
      		 if(dep==n+1){//只要满足搜索到的深度到了n+1了,就可以退回上一步了 
      		     ans++;//ans初始化为0 
      		     return;
      		 }
      		 dfs(dep+1,sum+a[dep]);// 买这一次的票 
      		 dfs(dep+1,sum);//不买这一次票 
      		}
      		int main(){
      		 read(n);read(m);
      		 for(R int i=1;i<=n;i++)read(a[i]);
      		 if(n<=20){
      		     dfs(1,0);  //从深度为1,到n+1, 一共有n次 
      		     printf("%lld\n",ans);
      		 }
      		    return 0;
      		} 
      		5-7组数据M≤106,裸的背包啊。
      			int main(){
      			 read(n);read(m);
      			 for(R int i=1;i<=n;i++)read(a[i]);
      			    if(m<=1e6){
      			        f[0]=1;
      			        for(R int i=1;i<=n;i++)
      			            for(R int j=m;j>=a[i];j--)
      			                f[j]+=f[j-a[i]];    //这一句到底是什么意思 
      			        for(R int i=0;i<=m;i++)ans+=f[i];
      			        printf("%lld\n",ans);
      			    }
      			    return 0;
      			}
      	``` 
      
      ![upload successful](\aoyue\images\pasted-188.png)
      
      ![upload successful](\aoyue\images\pasted-189.png)
      

#include

#include

#include

#include

#include

#define ll long long

#define R register

#define N 55
using namespace std;
templateinline void read(T &a){
char c=getchar();T x=0,f=1;
while(!isdigit(c)){if(c==’-‘)f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-‘0’;c=getchar();}
a=f*x;
}
ll n,m,w[N],mid,suma[1<<21],sumb[1<<21],cnta,cntb,ans;
inline void dfs(R int l,R int r,R ll sum,R ll a[],R ll &cnt){
if(sum>m)return;
if(l>r){
a[++cnt]=sum;
return;
}
dfs(l+1,r,sum+w[l],a,cnt);
dfs(l+1,r,sum,a,cnt);
}
int main(){
read(n);read(m);
for(R int i=1;i<=n;i++)read(w[i]);
mid=n>>1;
dfs(1,mid,0,suma,cnta);
dfs(mid+1,n,0,sumb,cntb);
sort(suma+1,suma+1+cnta);
for(R int i=1;i<=cntb;i++)
ans+=upper_bound(suma+1,suma+1+cnta,m-sumb[i])-suma-1;
printf(“%lld\n”,ans);
return 0;
}

tips:
在从小到大的排序数组中:
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
`

  • 7.P2572 [SCOI2010]序列操作

    区间染色问题,老司机树(Chtholly Tree (珂朵莉树) ODT)模板;思想和分块差不多,减少无效的访问次数
    [codeforces 896C Willem, Chtholly and Seniorious]

暴力可以加上点技巧(技巧性的暴力):如分块,前缀和,差分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值