1004 线性查找 O(n^3)
题意:有五个集合,每个集合中最多有200个数,从每个集合中选出一个数判断这五个数的和能否是零。
思路:如果直接暴力的话复杂度是O(n^5) TLE , 在网上看到一种所谓的线性查找的方法,刚开始跟这个思路差不多,但是还是TLE。。
把第一个集合的第二个集合的所有和的情况组成新的s1,第三个和第四个集合所有和的情况组成s2,然后对s1和s2分别排序 O(n^2 + n^n + nlgn + nlgn)
然后枚举最后一个集合的元素,对每一个元素,s1和s2一个从小枚举,一个从大枚举,如果s1[i] + s2[j] + tmp > 0 说明大的太大,j--; 如果小于零那么小的太小i++;这样最坏也就是2*n^n^n O(n^3)
参考于楼长:http://www.cnblogs.com/louzhang/archive/2012/08/02/2620588.html
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//typedef long long ll;
const int maxn = 210;
__int64 s1[maxn*maxn] , s2[maxn*maxn];
__int64 a[maxn] , b[maxn] , c[maxn] , d[maxn] , e[maxn];
int main() {
int T , cas = 1;
int n , i , j;
scanf("%d",&T);
while(T--) {
memset(s1 , 0 , sizeof(s1));
memset(s2 , 0 , sizeof(s2));
scanf("%d",&n);
for(i = 1 ; i <= n ; i ++)
scanf("%I64d",&a[i]);
for(i = 1 ; i <= n ; i ++)
scanf("%I64d",&b[i]);
for(i = 1 ; i <= n ; i ++)
scanf("%I64d",&c[i]);
for(i = 1 ; i <= n ; i ++)
scanf("%I64d",&d[i]);
for(i = 1 ; i <= n ; i ++)
scanf("%I64d",&e[i]);
int k = 0;
for(i = 1 ; i <= n ; i ++)
for(j = 1 ; j <= n ; j ++)
s1[k++] = a[i] + b[j];
int t = 0;
for(i = 1 ; i <= n ; i ++)
for(j = 1 ; j <= n ; j ++)
s2[t++] = c[i] + d[j];
sort(s1 , s1 + k);
sort(s2 , s2 + t);
sort(&e[1], e + n + 1);
int flag = 0;
int f;
for(f = 1 ; f <= n ; f ++) {
__int64 tmp = e[f]; //没有声明__in64 WA了好几次
i = 0 , j = t - 1;
while(i < k && j >= 0) {
if(s1[i] + s2[j] + tmp == 0 ) {
flag = 1;
break;
}
if(s1[i] + s2[j] + tmp > 0) j--;
else i ++;
}
if(flag) break;
}
if(flag) printf("Yes\n");
else printf("No\n");
}
}
1006 容斥原理
因为不想浪费那么最理想的情况就是s1∪s2∪s3...∪sn , si为在pi概率下要买的方便面袋数。
|A1∪A2∪...∪An| = Σ|Ai| - Σi~nΣj>i|Ai∩Aj| + (-1)^(n-1)|A1∩A2∩...∩An|
所以官方这个公式也就很裸了
还有就是求"C(n,2) C(n,3) ... C(n.n)" 是用dfs实现的
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
double s , P[25];
int k , n;
void dfs(int cnt , double p , int i) {
if(cnt == k) {
s += 1.0/p;
return;
}
for(int j = i ; j <= n ; j ++) {
dfs(cnt + 1 , p + P[j] , j + 1);
}
}
int main() {
int i;
while(~scanf("%d",&n)) {
double sum = 0;
for(i = 1 ; i <= n ; i ++) {
scanf("%lf",&P[i]);
sum += 1.0/P[i];
}
for(i = 2 ; i <= n ; i ++) {
k = i;
s = 0;
dfs(0 , 0 , 1);
if(i%2==0) s = (-1)*s;
sum += s;
}
printf("%lf\n",sum);
}
}
1009 线段树
题意:给出两个字符串,更新操作是更新1 a i c 代表把第a个string的第i号字符更新成字符c,询问操作是2 i代表从第一个字符串i开始去逐位匹配第二个字符串输出第一个失配的位置。
思路:很明显的线段树。。。有思路,还是搞不定这个线段树啊。。搓!后来看了下别人的思路搞了搞,可是有些细节还是处理不好。。。
主要参考:http://www.cnblogs.com/372465774y/archive/2012/08/04/2622549.html
主要就是不明白一点,在build和update 于PushUP结合的时候更新已经完成,询问的时候直接找cnt的值就完了呗,但是还是要判断在左区间的公共子串值是不是填满了左区间长度。。。不加就WA。。。 有点疑惑
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lson l , m , rt<<1
#define rson m + 1 , r , rt<<1|1
const int maxn = 1000010;
int cnt[maxn<<2]; // 以区间左端点为起点的公共子串长度
char s1[maxn] , s2[maxn];
void PushUP(int rt , int m , int l) {
//printf("Up %d\n",rt);
if(cnt[rt<<1] == (m - l + 1))
cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];
else cnt[rt] = cnt[rt<<1];
}
void build(int l , int r , int rt) {
if(l == r) {
if(s1[l] == s2[l]) {
cnt[rt] = 1;
}
else cnt[rt] = 0;
return;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUP(rt , m , l);
}
void Update(int i , int l , int r , int rt) {
if(l == r) {
if(s1[l] == s2[l]) cnt[rt] = 1;
else cnt[rt] = 0;
return;
}
int m = (l + r) >> 1;
if(i <= m) Update(i , lson);
else Update(i , rson);
PushUP(rt , m , l);
}
int Query(int i , int l , int r , int rt) {
if(i == l) return cnt[rt];
int m = (l + r) >> 1;
int ans;
if(i <= m) {
ans = Query(i , lson);
if(ans == m - i + 1) ans += cnt[rt<<1|1]; //感觉不用加 但是去掉就WA
}
else ans = Query(i , rson);
return ans;
}
int main() {
int T , cas = 1;
scanf("%d",&T);
while(T--) {
scanf("%s %s",s1,s2);
int Q;
int l1 , l2;
l1 = strlen(s1);
l2 = strlen(s2);
build(0 , l1 - 1, 1);
printf("Case %d:\n",cas++);
scanf("%d",&Q);
while(Q--) {
int t , i , j;
char c;
scanf("%d",&t);
if(t == 2) {
scanf("%d",&i);
if(i >= l1) printf("0\n");
else printf("%d\n",Query(i , 0 , l1 - 1, 1));
}
if(t == 1) {
scanf("%d %d %c",&i,&j,&c);
if(i == 1) s1[j] = c;
else s2[j] = c;
Update(j , 0 , l1 - 1 , 1);
}
}
}
}