老婆提醒的是- -该发解题报告了- -虽然是个弱菜。。。
A 计算几何
因为角度只有90 和 270 , 于是只用考虑 n = 4 6 8 的情况即可,传说特别恶心- -。。
B 网络流
传说是有上下界的费用流???图论不会
C 概率 + dp
当时比赛的时候没有写这个题目真心SB啊。。。
对于一个人在某个点的概率,那么就相当于在某个点有小数个人就可以了。
预处理:
1、离线点的位置,记录每个点上有多少个人。 我用了set + map
2、 gopre[j][i] , gonex[j][i] 数组代表 如果 第i个人是救助站的话, 之前 / 之后 到第j个人,一共需要多少花费 。n ^ 2
3、pre[i][j] 代表从第 i 个人到第 j 个人,要是只有一个救助站的话,需要多少路程期望。本来应该二分+验证的,不过有可能TLE。考虑到加了一个点的话肯定救助韩的位置要右移,所以直接一个指针跟着第二位运作就可以了。复杂度 n^2
dp:
dp[i][j] 代表前 i 个人要用 j 个救助站最小的费用期望。很明显 dp[i][j] = min{ dp[k][j - 1] + pre[k + 1][i]} n^2*m
const double eps = 1e-9; int dcmp(double d){ if (fabs(d) < eps) return 0; return d < 0 ? -1 : 1; } int n , m; map<int , double> Meng; set<int> zhu; set<int> :: iterator iter; const int N = 1001; int p[N]; double q[N]; double dp[N][51] , sumpre[N] , gopre[N][N] , gonex[N][N] , pre[N][N] ; double check(int l , int r, int p){ return gopre[l][p] + gonex[r][p]; } void solve(){ Meng.clear();zhu.clear(); while (n--){ int k , x; double y; scanf("%d" , &k); while (k--){ scanf("%d%lf",&x,&y); Meng[x] += y; zhu.insert(x); } } n = 0; memset(pre , 0 , sizeof(pre)); for (iter = zhu.begin() ; iter != zhu.end() ; iter ++){ if (n) sumpre[n] = sumpre[n - 1]; p[ n ] = (*iter); q[ n ] = Meng[p[n ++]]; sumpre[n - 1] += q[n - 1]; } memset(gopre , 0 , sizeof(gopre)); memset(gonex , 0 , sizeof(gonex)); for (int i = 0 ; i < n ; ++ i){ for (int j = i ; j >= 0 ; --j){ if (j < i) gopre[j][i] = gopre[j + 1][i] + q[j] * (p[i] - p[j]); } for (int j = i ; j < n ; ++j){ if (j > i) gonex[j][i] = gonex[j - 1][i] + q[j] * (p[j] - p[i]); } } int position = 0 ; for (int i = 0 ; i < n ; ++i){ position = i; for (int j = i ; j < n ; ++j){ while (position + 1 <= j){ double resl = check(i , j , position); double resr = check(i , j , position + 1); if (dcmp(resl - resr) >=0 ) position ++; else break; } pre[i][j] = check(i , j , position); } } for (int i = 0 ; i < n ; ++i) for (int j = 0 ; j <= m ; ++j) dp[i][j] = OO; for (int i = 0 ; i < n ; ++i) dp[i][1] = pre[0][i]; for (int i = 1 ; i < n ; ++i){ for (int j = 2 ; j <=m && j <= i + 1; ++j){ for (int k = j - 2 ; k < i ; ++k){ dp[i][j] = min(dp[i][j] , dp[k][j - 1] + pre[k + 1][i]); } } } printf("%.2f\n",dp[n - 1][m]); } int main(){ while (cin >> n >> m , n || m) solve(); }
E 憾失First Blood的大水题!!!!
找十字。。。尼玛很水的模拟题啊,我把一个加号写成了减号,于是我的FB啊!!!!
const int N = 100; char str[N][N]; int n; bool inmap(int x, int y){ return 0 <= x && x < n && 0 <= y && y < n; } const int dir[][2] = {{1 , 0} , {0 , 1} , {0 , -1} , {-1 , 0} }; bool check(int x, int y){ int j = 0 ; for (;; j++){ bool flag = false; for (int d = 0 ; d < 4 ; ++d){ flag |= (inmap(x + dir[d][0] * j , y + dir[d][1] * j) && str[x + dir[d][0] * j][y + dir[d][1] * j] =='#'); } if (!flag) break; if (flag){ for (int d = 0 ; d < 4 ; ++d){ flag &= (inmap(x + dir[d][0] * j , y + dir[d][1] * j) && str[x + dir[d][0] * j][y + dir[d][1] * j] =='#'); } } if (!flag) return false; } if (j <= 1) return false; for (int i = 1 ; i < j ; ++i){ int v1 = x + i; int v2 = x - i; if (str[v1][y-1] == '#' || str[v1][y + 1] =='#') return false; if (str[v2][y -1] =='#' || str[v2][y + 1] == '#') return false; int h1 = y + i; int h2 = y - i; if (str[x - 1][h1] == '#' || str[x +1 ][h1] == '#') return false; if (str[x -1 ][h2] == '#' || str[x +1 ][h2] == '#') return false; } return true; } void solve(){ for (int i = 0 ; i < n ; ++i){ scanf("%s",str[i]); } int ans = 0; for (int i = 0 ; i < n ; ++i){ for (int j = 0 ; j < n ; ++j){ if (str[i][j] == '#') { if (check(i , j)) { //printf("%d %d\n", i, j); ++ ans; } } } } printf("%d\n" , ans); } int main(){ while (cin >> n , n) solve(); }
【老婆你来搞定一下吧。。。。】
H 离线 / 划分树
离线的话,将所有的数字离线,将询问也按照c离线。每次增加一个询问的时候,把所有的 <= c 的数字都加到线段树 / 树状数组上面,然后求区间和。
划分树的话,(因为这个题目搞定了划分树)。对于某个区间,直接二分第k大 >= c 。
#define M 100011 struct Seg_Tree{ int left,right; int mid() { return (left + right) >> 1; } }tt[M*4]; int len; int sorted[M]; //元素放入,排序 int toLeft[30][M]; int val[30][M]; //树, 读入val[0][i] , 从 1 开始 void build(int l,int r,int d,int idx) { tt[idx].left = l; tt[idx].right = r; if(tt[idx].left == tt[idx].right) return ; int mid = tt[idx].mid(); int lsame = mid - l + 1;//lsame表示和val_mid相等且分到左边的 for(int i = l ; i <= r ; i ++) { if(val[d][i] < sorted[mid]) { lsame --;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去 } } int lpos = l; int rpos = mid+1; int same = 0; for(int i = l ; i <= r ; i ++) { if(i == l) { toLeft[d][i] = 0;//toLeft[i]表示[ tt[idx].left , i ]区域里有多少个数分到左边 } else { toLeft[d][i] = toLeft[d][i-1]; } if(val[d][i] < sorted[mid]) { toLeft[d][i] ++; val[d+1][lpos++] = val[d][i]; } else if(val[d][i] > sorted[mid]) { val[d+1][rpos++] = val[d][i]; } else { if(same < lsame) {//有lsame的数是分到左边的 same ++; toLeft[d][i] ++; val[d+1][lpos++] = val[d][i]; } else { val[d+1][rpos++] = val[d][i]; } } } build(l,mid,d+1, idx << 1); build(mid+1,r,d+1, idx << 1 | 1); } int query(int l,int r,int k,int d,int idx) { if(l == r) { return val[d][l]; } int s;//s表示[ l , r ]有多少个分到左边 int ss;//ss表示 [tt[idx].left , l-1 ]有多少个分到左边 if(l == tt[idx].left) { s = toLeft[d][r]; ss = 0; } else { s = toLeft[d][r] - toLeft[d][l-1]; ss = toLeft[d][l-1]; } if(s >= k) {//有多于k个分到左边,显然去左儿子区间找第k个 int newl = tt[idx].left + ss; int newr = tt[idx].left + ss + s - 1;//计算出新的映射区间 return query(newl,newr,k,d+1, idx << 1); } else { int mid = tt[idx].mid(); int bb = l - tt[idx].left - ss;//bb表示 [tt[idx].left , l-1 ]有多少个分到右边 int b = r - l + 1 - s;//b表示 [l , r]有多少个分到右边 int newl = mid + bb + 1; int newr = mid + bb + b; return query(newl,newr,k-s,d+1, idx << 1 | 1); } } int _ , __; int n , m; void solve(){ scanf("%d%d" , &n, &m); for (int i = 1 ; i <= n ; ++i){ scanf("%d" , &val[0][i]); //读入树 sorted[i] = val[0][i]; //排序 } sort(sorted + 1 , sorted + n + 1); //排序 build(1 , n , 0 , 1); // 建树 printf("Case %d:\n",++__); while (m --){ int l , r , h; scanf("%d%d%d" ,&l , &r , &h); l ++ ; r ++; int low = 1 , high = r - l + 1 , mid , ret = 0; do{ mid = (low + high) >> 1; if (query(l , r , mid , 0 , 1) <= h){ //必须至少 k = 1 ret = max(ret , mid); low = mid + 1; } else high = mid - 1; }while (low <= high); printf("%d\n", ret); } } int main(){ cin >> _; __ = 0; while (_ -- ) solve(); } /* int main() { int T; scanf("%d",&T); while(T --) { int n , m; scanf("%d%d",&n,&m); FOR(i,1,n+1) { scanf("%d",&val[0][i]); sorted[i] = val[0][i]; } sort(sorted + 1 , sorted + n + 1); build(1,n,0,1); while(m --) { int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\n",query(l,r,k,0,1)); } } return 0; }*/
J 容斥原理 + 矩阵面积并
一开始还想写个特别NB的线段树。。。真心SB。。。
将 {R} {B} {G} {RG} {RB} {GB} {RGB} 颜色的矩形分别作面积并。然后就是很神奇的演算!!!具体在代码里面有写。
double过不了啊!!不能偷懒不改模板- -
#include <cstdio> #include <cstring> #include <cctype> #include <algorithm> #include <iostream> #include <math.h> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 #define LL __int64 const int maxn = 22222; int cnt[maxn << 2]; LL sum[maxn << 2]; LL X[maxn]; struct Seg { LL h , l , r; int s; Seg(){} Seg(LL a,LL b,LL c,LL d) : l(a) , r(b) , h(c) , s(d) {} bool operator < (const Seg &cmp) const { return h < cmp.h; } }ss[maxn]; void PushUp(int rt,int l,int r) { if (cnt[rt]) sum[rt] = X[r+1] - X[l]; else if (l == r) sum[rt] = 0; else sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void update(int L,int R,LL c,int l,int r,int rt) { if (L <= l && r <= R) { cnt[rt] += c; PushUp(rt , l , r); return ; } int m = (l + r) >> 1; if (L <= m) update(L , R , c , lson); if (m < R) update(L , R , c , rson); PushUp(rt , l , r); } int Bin(LL key,int n,LL X[]) { int l = 0 , r = n - 1; while (l <= r) { int m = (l + r) >> 1; if (X[m] - key == 0 ) return m; if (X[m] - key < 0) l = m + 1; else r = m - 1; } return -1; } int m; LL cal(){ sort(X , X + m); sort(ss , ss + m); int k = 1; for (int i = 1 ; i < m ; i ++) { if (X[i] != X[i-1]) X[k++] = X[i]; } memset(cnt , 0 , sizeof(cnt)); memset(sum , 0 , sizeof(sum)); LL ret = 0; for (int i = 0 ; i < m - 1 ; i ++) { int l = Bin(ss[i].l , k , X); int r = Bin(ss[i].r , k , X) - 1; if (l <= r) update(l , r , ss[i].s , 0 , k - 1, 1); ret += sum[1] * (ss[i+1].h - ss[i].h); } return ret; } char temp[2]; const int N = 200000; struct Meng{ char op; LL a ,b ,c ,d; void input(){ scanf("%s%I64d%I64d%I64d%I64d",temp ,&a,&b,&c,&d); op = temp[0]; } }p[N]; int main() { int n , cas = 1; int _; cin >> _; while (_--) { scanf("%d",&n); LL r=0 , g=0 , b=0 , rg=0 , rb=0 , gb=0 , rgb=0; for (int i = 0 ; i < n ; ++i) p[i].input(); m=0; for (int i = 0 ; i < n ; ++i){ if (p[i].op == 'R'){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } r = cal(); m=0; for (int i = 0 ; i < n ; ++i){ if (p[i].op == 'G'){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } g = cal(); m=0; for (int i = 0 ; i < n ; ++i){ if (p[i].op == 'B'){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } b = cal(); m=0; for (int i = 0 ; i < n ; ++i){ if (p[i].op == 'R' || p[i].op == 'G'){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } rg = cal(); m=0; for (int i = 0 ; i < n ; ++i){ if (p[i].op == 'R' || p[i].op == 'B'){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } rb = cal(); m=0; for (int i = 0 ; i < n ; ++i){ if (p[i].op == 'G' || p[i].op == 'B'){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } gb = cal(); m=0; for (int i = 0 ; i < n ; ++i){ if (1){ LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d; X[m] = a; ss[m++] = Seg(a , c , b , 1); X[m] = c; ss[m++] = Seg(a , c , d , -1); } } rgb = cal(); //cout << rgb << endl; printf("Case %d:\n", cas++); LL R = rgb - gb , G = rgb - rb , B = rgb - rg; //printf("%.0lf\n%.0lf\n%.0lf\n%.0lf\n%.0lf\n%.0lf\n%.0lf\n",r,g,b,rg,rb,gb,rgb); printf("%I64d\n%I64d\n%I64d\n%I64d\n%I64d\n%I64d\n%I64d\n",rgb - gb , rgb - rb , rgb - rg , rgb - b - R - G , rgb - g - R - B , rgb - r - G - B , r - (rgb - b - R - G) - (rgb - g - R - B) - R ); //printf("\n\n"); //printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ret); } return 0; } /* 3 2 R 0 0 2 2 G 1 1 3 3 3 R 0 0 4 4 G 2 0 6 4 B 0 2 6 6 3 G 2 0 3 8 G 1 0 6 1 B 4 2 7 7 */
总结:
比赛的时候还是太不沉稳了。F题以前在UESTC上面做过原题,就觉得这个题目肯定可以出。将近有两个小时完全没心思看别的!!!这是2B行为!!绝对的!!以后这种问题这个题目直接交给队友,然后怒虐别的题目。放弃一个题目不可惜,因为卡在了一个题目上面而丢掉了N个题目和各种时间才可惜- -!!还是要加强啊。。。