The 2017 ACM-ICPC Asia Beijing Regional Contest

传送门

C - Graph

题意:
给出一个\(n\)个点\(m\)条边的无向图。现在有多组询问,每组询问给出区间\([l,r]\),问区间\([l,r]\)中有多少点对是连通的。

思路:

  • 若考虑只有一组询问的情况,那么显然我们直接用并查集搞搞就行,复杂度为\(O(mlogn)\)
  • 多组询问直接暴力上复杂度显然不够,所以现在有一种新姿势:回滚莫队。
  • 这个题的特点:多组区间询问,不带修改,那么我们就可以考虑莫队。
  • 但是区间端点移动时可能会涉及到撤销操作,但是对于并查集而言,我们不能直接从中间撤销。
  • 所以回滚莫队搞一下即可,就左端点滚过去,更新完后滚回来同时撤销刚才的操作即可。

我们分块时按照点的度数来分块,本质上是按边的个数来划分,使得每一个块中边的个数之和约为\(\sqrt{2m}\)
代码如下:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 50000 + 5, M = 1e5 + 5;

int n, m, q, blo;
int d[N], f[N], in[N], rb[N], sz[N];
int sta[N], top;
int res;
struct Edge{
	int v, next;
}e[N << 1];
int head[N], tot;
void adde(int u, int v) {
	e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
int ans[M];
struct Query{
	int l, r, id;
	bool operator < (const Query &A) const {
		if(in[l] != in[A.l]) return in[l] < in[A.l];
		return r < A.r;
	}
}Q[M];
int find(int x) {
	return f[x] == x ? f[x] : find(f[x]);
}
void Union(int x, int y, int op) {
	int fx = find(x), fy = find(y);
	if(fx == fy) return;
	if(sz[fx] > sz[fy]) {
		swap(x, y); swap(fx, fy);
	}
	res += sz[fx] * sz[fy];
	f[fx] = fy;
	sz[fy] += sz[fx];
	if(!op) sta[++top] = fx;
}
void cancel() {
	while(top) {
		int u = sta[top--];
		sz[f[u]] -= sz[u];
		res -= sz[f[u]] * sz[u];
		f[u] = u;
	}
}
void run() {
	cin >> n >> m >> q;
	for(int i = 1; i <= n; i++) d[i] = 0, head[i] = -1;
	tot = top = 0;
	for(int i = 1; i <= m; i++) {
		int u, v; cin >> u >> v;
		adde(u, v); adde(v, u);
		++d[u]; ++d[v];
	}
	int totd = 0;
	int blo = sqrt(2 * m + 0.5) + 1;
	for(int i = 1; i <= n; i++) {
		totd += d[i];
		in[i] = totd / blo;
		rb[in[i]] = i;
	}
	for(int i = 1; i <= q; i++) {
		cin >> Q[i].l >> Q[i].r;
		Q[i].id = i;
	}
	sort(Q + 1, Q + q + 1);
	int b = -1, L, R;
	for(int i = 1; i <= q; i++) {
		int l = Q[i].l, r = Q[i].r, id = Q[i].id;
		if(b != in[l]) {
			b = in[l];
			L = R = rb[b];
			res = 0;
			for(int j = 1; j <= n; j++) f[j] = j, sz[j] = 1;
		}
		while(R < r) {
			for(int j = head[++R]; j != -1; j = e[j].next) {
				int v = e[j].v;
				if(v > L && v <= r) Union(R, v, 1);
			}
		}
		for(L = min(r, L); L >= l; L--) {
			for(int j = head[L]; j != -1; j = e[j].next) {
				int v = e[j].v;
				if(v >= l && v <= r) Union(L, v, 0);
			}
		}
		ans[id] = res;
		cancel(); 
		L = rb[b];
	}
	for(int i = 1; i <= q; i++) cout << ans[i] << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;
}

E - Cats and Fish

模拟题意即可。

F - Secret Poems

模拟。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head

char mtx[107][107];
char st[107*107];
    int n;
inline char rc() {
    static char c; do c=getchar(); while(c<=' ');
    return c;
}

inline void getst1() {
    int x=0,y=0;
int chn=0;
    #define U() do{x--;st[chn++]=mtx[x][y];}while(0)
    #define D() do{x++;st[chn++]=mtx[x][y];}while(0)
    #define L() do{y--;st[chn++]=mtx[x][y];}while(0)
    #define R() do{y++;st[chn++]=mtx[x][y];}while(0)
    #define RU() do{y++;x--;st[chn++]=mtx[x][y];}while(0)
    #define DL() do{x++;y--;st[chn++]=mtx[x][y];}while(0)
        st[chn++]=mtx[x][y];
        if(n%2==0) {
            REP(i,1,n) {
                if((i&1) ==1) {
                    R(); REP(j,0,i)DL();
                } else {
                    D(); REP(j,0,i)RU();
                }
            }
            PERE(i,n-2,0) {
                if((i&1)==0) {
                    R(); REP(j,0,i)RU();
                } else {
                    D(); REP(j,0,i)DL();
                }
            }
        } else {
            REP(i,1,n) {
                if((i&1)==1) {
                    R(); REP(j,0,i)DL();
                } else {
                    D(); REP(j,0,i)RU();
                }
            }
            PERE(i,n-2,0) {
                if((i&1)==1) {
                    D(); REP(j,0,i)DL();
                } else {
                    R(); REP(j,0,i)RU();
                }
            }
        }
        st[chn]=0;
        #undef U
        #undef D
        #undef L
        #undef R
        #undef RU
        #undef DL
}
inline void writest() {
    int x=0,y=0;
    int chn=0;
    #define WR() do{mtx[x][y]=st[chn++];}while(0)
    #define R() do{y++;WR();}while(0)
    #define L() do{y--;WR();}while(0)
    #define D() do{x++;WR();}while(0)
    #define U() do{x--;WR();}while(0)
    WR();
    REP(i,1,n)R();
    int k=0;
    PERE(i,n-1,0) {
        if(k==0) {
            REP(j,0,i) D();
            k++; i++;
        } else if(k==1){
            REP(j,0,i) L();
            k++;
        } else if(k==2){
            REP(j,0,i) U();
            k++; i++;
        } else if(k==3){
            REP(j,0,i) R();
            k=0;
        }
    }
}
int main() {
    //ios::sync_with_stdio(false);
    //cin.tie(0); cout.tie(0);
    //cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(~scanf("%d", &n)) {
        REP(i,0,n)REP(j,0,n) mtx[i][j]=rc();


        getst1();
        writest();

        REP(i,0,n) {
            REP(j,0,n) {
                putchar(mtx[i][j]);
            }
            putchar('\n');
        }
    }
    return 0;
}

G - Liaoning Ship’s Voyage

BFS+判断线段相交。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...) (void)0
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head

inline char gch() {
    static char x; do x=getchar(); while(x<=' ');
    return x;
}

#define EPS 1e-6
inline int dcmp(double x) {
    if(fabs(x)<EPS) {
        return 0;
    }
    return x<0?-1:1;
}

struct Point {
    double x,y;
};
inline Point operator-(const Point&l, const Point &r) {
    return (Point){l.x-r.x, l.y-r.y};
}

Point sjx[3];
char mp[27][27];
int dis[27][27];
int n;

inline double cross(Point a, Point b) {
    return a.x*b.y-a.y*b.x;
}

inline double dot(Point a, Point b) {
    return a.x*b.x+a.y*b.y;
}

bool segpsec(Point a,Point b,Point c,Point d)
{
    int d1=dcmp(cross(b-a,c-a)); //b-a, c-a
    int d2=dcmp(cross(b-a,d-a)); //b-a d-a
    int d3=dcmp(cross(d-c,a-c)); //d-c a-c
    int d4=dcmp(cross(d-c,b-c)); //d-c b-c
    return (d1^d2) == -2 && (d3^d4) == -2;
}

bool onseg(Point a, Point b, Point p) {
     return dcmp(cross(p-a,b-a)) == 0 && dcmp(dot(p-a,p-b)) <= 0;
}

bool segsec(Point a,Point b,Point c,Point d)
{
    if (segpsec(a,b,c,d)) return true;
    return onseg(a,b,c) || onseg(a,b,d) || onseg(c,d,a) || onseg(c,d,b);
}

Point llsec(Point a, Point b, Point c, Point d) {
    double a1=cross(d-c,a-c);
    double a2=cross(d-c,b-c);
    return (Point){(a.x*a2-b.x*a1)/(a2-a1),(a.y*a2-b.y*a1)/(a2-a1 )};
}

inline bool in(int x, int y) {
    Point k=(Point){x,y};
    int sgn1=dcmp(cross(sjx[0]-sjx[1],k-sjx[1]));
    int sgn2=dcmp(cross(sjx[1]-sjx[2],k-sjx[2]));
    int sgn3=dcmp(cross(sjx[2]-sjx[0],k-sjx[0]));
    if(sgn1==sgn2 && sgn2==sgn3) return true;
    return false;
}

inline bool in2(double x, double y) {
    Point k=(Point){x,y};
    int sgn1=dcmp(cross(sjx[0]-sjx[1],k-sjx[1]));
    int sgn2=dcmp(cross(sjx[1]-sjx[2],k-sjx[2]));
    int sgn3=dcmp(cross(sjx[2]-sjx[0],k-sjx[0]));
    if(sgn1==sgn2 && sgn2==sgn3) return true;
    return false;
}

inline void draw() {
    REP(i,0,n) REP(j,0,n) {
        if(in(i,j)) {
            mp[i][j]='#';
        }
    }
}
struct node {
    int x,y;
};
queue<node> q;
inline void bfs() {
    if(mp[0][0]=='#') {
        puts("-1");
        return;
    }
    REP(i,0,n) REP(j,0,n) {
        dis[i][j]=0x3f3f3f3f3f;
    }
    dis[0][0]=0;
    q.push((node){0,0});
    const int dx[]={0,1,1,1,0,-1,-1,-1};
    const int dy[]={1,1,0,-1,-1,-1,0,1};
    while(!q.empty()) {
        node now=q.front(); q.pop();
        REP(i,0,8) {
            node nxt=(node){now.x+dx[i], now.y+dy[i]};
            if(nxt.x<n&&nxt.x>=0 &&
                nxt.y<n && nxt.y>=0 &&
                mp[nxt.x][nxt.y]=='.' &&
                dis[nxt.x][nxt.y]>dis[now.x][now.y]+1) {
                Point z1=(Point){now.x,now.y};
                Point z2=(Point){nxt.x,nxt.y};
                bool go1=segpsec(z1,z2,sjx[0],sjx[1]);
                bool go2=segpsec(z1,z2,sjx[1],sjx[2]);
                bool go3=segpsec(z1,z2,sjx[2],sjx[0]);
                if(go1 || go2 || go3) continue;
                Point ps[3]; int pn=0;
                if(segsec(z1,z2,sjx[0],sjx[1]))
                    if(dcmp(cross(z2-z1,sjx[0]-sjx[1]))!=0) {ps[pn++]=llsec(z1,z2,sjx[0],sjx[1]);}
                if(segsec(z1,z2,sjx[1],sjx[2]))
                    if(dcmp(cross(z2-z1,sjx[1]-sjx[2]))!=0) {ps[pn++]=llsec(z1,z2,sjx[1],sjx[2]);}
                if(segsec(z1,z2,sjx[2],sjx[0]))
                    if(dcmp(cross(z2-z1,sjx[2]-sjx[0]))!=0) {ps[pn++]=llsec(z1,z2,sjx[2],sjx[0]);}
                if(pn==3) continue;
                if(pn==2 && in2((ps[0].x+ps[1].x)/2 , (ps[0].y+ps[1].y)/2)) {
                    continue;
                }
                dis[nxt.x][nxt.y]=dis[now.x][now.y]+1;
                q.push(nxt);
            }
        }
    }
    if(dis[n-1][n-1]<0x3f3f3f3f) {
        printf("%d\n", dis[n-1][n-1]);
    } else {
        puts("-1");
    }
}

int main() {
    //ios::sync_with_stdio(false);
    //cin.tie(0); cout.tie(0);
    //cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(~scanf("%d", &n)) {
        REP(i,0,3) scanf("%lf%lf", &sjx[i].x, &sjx[i].y);
        //REP(i,0,n+1) REP(j,0,n+1) mp[i][j]='.';
        REP(i,0,n) REP(j,0,n) {
            mp[j][n-1-i]=gch();
        }

        draw();
        #ifdef Local
        REP(i,0,n) {
            REP(j,0,n) {
                putchar(mp[j][n-1-i]);
            }
            putchar('\n');
        }
        #endif
        bfs();
    }
    return 0;
}

H - Puzzle Game

题意:
给出一个\(n*m\)的矩阵,每个位置有对应权值,可能为负。
现在给定一个\(p\),能替换一个位置的权值。问最后的最大子矩阵最小权值为多少。

思路:

  • 考虑枚举每个位置进行修改,然后快速维护答案;
  • 显然修改操作只会影响包含当前位置的最大子矩阵,我们还需要快速求出其余位置的最大子矩阵。
  • 那么我们预处理出\(up,down,left,right\)表示四个方向的最大子矩阵,就可以快速求出不包含当前点的答案了。这可以直接\(O(n^3)\)预处理。
  • 包含当前位置的最大子矩阵,我们可以任选一个最大子矩阵,然后在其内部枚举即可。
  • 正确性?
  • 对于与其重合的最大子矩阵,我们枚举时会考虑到交点;否则,其余的最大子矩阵,我们求出其余方向的矩阵时会考虑到。

简单说就直接考虑包含和不包含两种情况,不包含情况预处理出来,包含的情况直接考虑最大子矩阵即可。
预处理可以枚举两行、两列然后求最大子序列和。
详见代码:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
// #define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 155;

int n, m, p;
int a[N][N];
int Up[N], Down[N], Left[N], Right[N];
int u, d, l, r;
int sum[N], res[N];

int calc() {
	int tot = -INF;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) sum[j] = 0;
		for(int j = i; j <= n; j++) {
			for(int k = 1; k <= m; k++) sum[k] += a[j][k];
			int p = 0;
			for(int k = 1; k <= m; k++) {
				res[k] = res[k - 1] + sum[k];
				if(res[k] - res[p] > tot) {
					tot = res[k] - res[p];
					u = i, d = j, l = p + 1, r = k;
				}
				if(res[k] < res[p]) p = k;
			}
		}
	}
	dbg(u, d, l, r, tot);
	return tot;
}

void calc1() {
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) sum[j] = 0;
		for(int j = i; j <= n; j++) {
			int tot = -INF;
			for(int k = 1; k <= m; k++) sum[k] += a[j][k];
			int p = 0;
			for(int k = 1; k <= m; k++) {
				res[k] = res[k - 1] + sum[k];
				if(res[k] - res[p] > tot) {
					tot = res[k] - res[p];
					Down[i] = max(Down[i], tot);
					Up[j] = max(Up[j], tot);
				}
				if(res[k] < res[p]) p = k;
			}
		}
	}
	for(int i = 2; i <= n; i++) Up[i] = max(Up[i], Up[i - 1]);
	for(int i = n - 1; i >= 1; i--) Down[i] = max(Down[i], Down[i + 1]);
}

void calc2() {
	for(int i = 1; i <= m; i++) {
		for(int j = 1; j <= n; j++) sum[j] = 0;
		for(int j = i; j <= m; j++) {
			int tot = -INF;
			for(int k = 1; k <= n; k++) sum[k] += a[k][j];
			int p = 0;
			for(int k = 1; k <= n; k++) {
				res[k] = res[k - 1] + sum[k];
				if(res[k] - res[p] > tot) {
					tot = res[k] - res[p];
					Left[j] = max(Left[j], tot);
					Right[i] = max(Right[i], tot);
				}
				if(res[k] < res[p]) p = k;
			}
		}
	}
	for(int i = 2; i <= m; i++) Left[i] = max(Left[i], Left[i - 1]);
	for(int i = m - 1; i >= 1; i--) Right[i] = max(Right[i], Right[i + 1]);
}

void run() {
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			cin >> a[i][j];
		}
	}
	for(int i = 0; i <= n + 1; i++) Up[i] = Down[i] = -INF;
	for(int i = 0; i <= m + 1; i++) Left[i] = Right[i] = -INF;
 	int Max = calc();
	calc1();
	calc2();
	dbg(Up[1], Right[3], Down[2], Left[2]);
	int ans = Max;
	for(int i = u; i <= d; i++) {
		for(int j = l; j <= r; j++) {
			if(a[i][j] <= p) continue;
			ans = min(ans, max(Max - a[i][j] + p, max(Up[i - 1], 
				max(Down[i + 1], max(Left[j - 1], Right[j + 1])))));
		}
	}
	cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n >> m >> p) run();
    return 0;
}

J - Pangu and Stones

题意:
给出\(n\)堆石子,每堆石子有\(a_i\)个。现在每次可以选择\(l\)~\(r\)堆的石子进行合并,问最后合并为一堆石子的最小代价为多少;不能合并为一堆则输出\(-1\)

思路:

  • 定义\(dp(l,r,k)\)表示考虑将区间\([l,r]\)分为\(k\)堆的最小代价。
  • 如果我们已经确定了一种划分方式,那么直接合并起来,代价为\(sum_r-sum_{l-1}\)
  • 假设现在\(k=1\),显然我们要将\([l,r]\)个数的堆合并为一堆,注意到对于所有的\(x\)堆合并为一堆,我们可以将其划分为\(x-1\)堆和\(1\)堆的合并;
  • \(k>1\),此时不考虑合并,考虑通过一种最小的代价将其划分为\(k\)堆,同样地,我们通过枚举中间点,然后将序列划分为\(k-1\)\(1\)堆,这能覆盖到所有的情况。

所以\(dp\)的状态转移方程为:

\[\left\{ \begin{aligned} &dp[i][j][1]=min\{dp[i][k][x-1]+dp[k+1][j][1]+sum_{j}-sum_{i-1}\}\\ &dp[i][j][x]=min\{dp[i][k][x-1]+dp[k+1][j][1],x>1\} \end{aligned} \right. \]

详见代码:(感觉说不清楚QAQ)

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
// #define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 105;

int n, l, r;
int a[N], sum[N];
int dp[N][N][N];

void run() {
	for(int i = 1; i <= n; i++) {
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
	}
	for(int i = 1; i <= n; i++) 
		for(int j = 1; j <= n; j++) 
			for(int k = 0; k <= n; k++) 
				dp[i][j][k] = INF;	
	for(int i = 1; i <= n; i++) {
		dp[i][i][1] = 0;
	}
	for(int len = 2; len <= n; len++) {
		for(int i = 1; i + len - 1 <= n; i++) {
			int j = i + len - 1;
			for(int k = i; k < j; k++) {
				for(int t = l - 1; t <= r - 1; t++) {
					if(k - i + 1 >= t)
						dp[i][j][1] = min(dp[i][j][1], dp[i][k][t] + dp[k + 1][j][1] + sum[j] - sum[i - 1]);
				}
			}
			for(int t = 2; t <= j - i + 1; t++) {
				for(int k = i; k < j; k++) {
					if(k - i + 1 >= t - 1)
					dp[i][j][t] = min(dp[i][j][t], dp[i][k][t - 1] + dp[k + 1][j][1]);
				}
			}
		}
	}
	if(dp[1][n][1] == INF) dp[1][n][1] = 0;
	pt(dp[1][n][1]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    while(cin >> n >> l >> r) run();
    return 0;
}

posted @ 2019-10-22 23:38  heyuhhh  阅读(249)  评论(0编辑  收藏  举报