老婆提醒的是- -该发解题报告了- -虽然是个弱菜。。。
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();
}
F 贪心
【老婆你来搞定一下吧。。。。】
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个题目和各种时间才可惜- -!!还是要加强啊。。。