2017-2018 ACM-ICPC East Central North America Regional Contest (ECNA 2017) 部分题解

contest 链接:http://codeforces.com/gym/101673

 

A - Abstract Art

求多个多边形并的面积,板子题,记小本本

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long ll;
const double inf=1e200;
const double eps=1e-12;
const double pi=4*atan(1.0);
int dcmp(double x){ return fabs(x)<eps?0:(x<0?-1:1);}
struct point{
    double x,y;
    point(double a=0,double b=0):x(a),y(b){}
};
point operator +(point A,point B) { return point(A.x+B.x,A.y+B.y);}
point operator -(point A,point B) { return point(A.x-B.x,A.y-B.y);}
point operator *(point A,double p){ return point(A.x*p,A.y*p);}
point operator /(point A,double p){ return point(A.x/p,A.y/p);}
bool operator ==(const point& a,const point& b){
    return fabs(a.x-b.x)<eps&&fabs(a.y-b.y)<eps;
}
double dot(point A,point B){ return A.x*B.x+A.y*B.y;}
double det(point A,point B){ return A.x*B.y-A.y*B.x;}
double det(point O,point A,point B){ return det(A-O,B-O);}
double length(point A){ return sqrt(dot(A,A));}
double area(vector<point>p){
    double ans=0; int sz=p.size();
    for(int i=1;i<sz-1;i++) ans+=det(p[i]-p[0],p[i+1]-p[0]);
    return ans/2.0;
}
double seg(point O,point A,point B){
    if(dcmp(B.x-A.x)==0) return (O.y-A.y)/(B.y-A.y);
    return (O.x-A.x)/(B.x-A.x);
}
vector<point>pp[110];
pair<double,int>s[110*60];
double polyunion(vector<point>*p,int N){
    double res=0;
    for(int i=0;i<N;i++){
        int sz=p[i].size();
        for(int j=0;j<sz;j++){
            int m=0;
            s[m++]=mp(0,0);
            s[m++]=mp(1,0);
            point a=p[i][j],b=p[i][(j+1)%sz];
            for(int k=0;k<N;k++){
                if(i!=k){
                    int sz2=p[k].size();
                    for(int ii=0;ii<sz2;ii++){
                        point c=p[k][ii],d=p[k][(ii+1)%sz2];
                        int c1=dcmp(det(b-a,c-a));
                        int c2=dcmp(det(b-a,d-a));
                        if(c1==0&&c2==0){
                            if(dcmp(dot(b-a,d-c))){
                                s[m++]=mp(seg(c,a,b),1);
                                s[m++]=mp(seg(c,a,b),-1);
                            }
                        }
                        else{
                            double s1=det(d-c,a-c);
                            double s2=det(d-c,b-c);
                            if(c1>=0&&c2<0) s[m++]=mp(s1/(s1-s2),1);
                            else if(c1<0&&c2>=0) s[m++]=mp(s1/(s1-s2),-1);
                        }
                    }
                }    
            }
            sort(s,s+m);
            double pre=min(max(s[0].first,0.0),1.0),now,sum=0;
            int cov=s[0].second;
            for(int j=1;j<m;j++){
                now=min(max(s[j].first,0.0),1.0);
                if(!cov) sum+=now-pre;
                cov+=s[j].second;
                pre=now;
            }
            res+=det(a,b)*sum;
        }
    }
    return res/2;
}
int main()
{
    int N,M,i,j; point tp;
    scanf("%d",&N);
    for(i=0;i<N;i++){
        scanf("%d",&M);
        for(j=0;j<M;j++){
            scanf("%lf%lf",&tp.x,&tp.y);
            pp[i].push_back(tp);
        }
    }
    double t1=0,t2=polyunion(pp,N);
    for(i=0;i<N;i++) t1+=area(pp[i]);
    printf("%.7lf %.7lf\n",-t1,-t2);
    return 0;
}

 

C - DRM Messages

很水的小模拟

在不知道题意的情况下听队友说步骤就过了

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e3 + 5;
using namespace std;

int main()
{
	string s;
	cin >> s;
	int len = s.size();
	int sum = 0;
	for(int i = 0; i < len / 2; i++){
		sum += s[i] - 'A';
	}
	for(int i = 0; i < len / 2; i++){
		s[i] = (s[i] - 'A' + sum) % 26 + 'A';
	}
	sum = 0;
	for(int i = len / 2; i < len; i++){
		sum += s[i] - 'A';
	}
	for(int i = len / 2; i < len; i++){
		s[i] = (s[i] - 'A' + sum) % 26 + 'A';
	}
	for(int i = 0; i < len / 2; i++){
		s[i] = (s[i] - 'A' + s[i+len/2] - 'A') % 26 + 'A';
	}
	for(int i = 0; i < len / 2; i++){
		cout << s[i];
	}
	cout << endl;
	return 0;
}

 

D - Game of Throwns

这也是个水题啊,用栈模拟一下就好了

但还是因为取模没考虑负数WA了两发

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e3 + 5;
using namespace std;

stack<int> st;

int main()
{
	int n, k;
	cin >> n >> k;
	for(int i = 1; i <= k; i++){
		string s; cin >> s;
		if(s == "undo"){
			int cnt;
			cin >> cnt;
			while(cnt--){
				st.pop();
			}
		}
		else{
			stringstream ss;
			ss << s;
			int x;
			ss >> x;
			st.push(x);
		}
	}
	int ans = 0;
	while(!st.empty()){
		ans += st.top();
		st.pop();
	}
	cout << (ans + 3000 * n) % n << endl;
}

 

H - Sheba's Amoebas

题意:求出黑色环的个数, 数据保证不相交

解题思路:其实也就是个简单的dfs,因为要判断环,多开一个vis数组标记一下即可

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e3 + 5;
using namespace std;

char G[110][110];
int dir[8][2] = { {1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1} };
bool vis1[110][110], vis2[110][110];
int n, m;
int startx, starty;
int ans;

bool check(int x, int y){
	if(x >= 1 && x <= n & y >= 1 && y <= m && G[x][y] == '#' && !vis1[x][y] & ! vis2[x][y]) return true;
	return false;
}

void dfs(int x, int y, int cnt){
	bool flag = false;
	for(int i = 0; i <= 7; i++){
		int tx = x + dir[i][0], ty = y + dir[i][1];
		if(check(tx, ty)){
			vis1[tx][ty] = vis2[tx][ty] = 1;
			dfs(tx, ty, cnt + 1);
			vis1[tx][ty] = 0;
			flag = true;
		}
	}
	if(!flag && cnt > 1)
		for(int i = 0; i <= 7; i++){
			int tx = x + dir[i][0], ty = y + dir[i][1];
			if(tx == startx && ty == starty){
				ans++;
				return;
			}
		}
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			scanf(" %c", &G[i][j]);
		}
	}
	ans = 0;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(!vis1[i][j] && G[i][j] == '#'){
				memset(vis1, 0, sizeof(vis1));
				vis1[i][j] = vis2[i][j] =  true;
				startx = i, starty = j;
				dfs(i, j, 1);
			}
		}
	}
	printf("%d\n", ans);
	return 0;
}

 

好啦,无脑题都说完啦

G - A Question of Ingestion

题目大意:一开始的食量为m,有n个连续的小时,每小时都有一个a[i]表示,可以选择吃或者不吃。

这个小时吃的话下个小时的食量就变成2/3,上个小时吃了这个小时不吃的话下个小时恢复到上个小时的,连续两小时不吃就能恢复到m

解题思路:开始题意理解错了,这么简单的一个记忆化搜索都没做出来,伤心。。

直接记忆化搜索枚举不吃的小时即可

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

int a[105];
int dp[105][20005];
int n, m;

int dfs(int i, int j){
	if(i > n) return 0;
	if(dp[i][j] != -1) return dp[i][j];
	dp[i][j] = min(a[i], j) + max(dfs(i + 1, j * 2 / 3), max(dfs(i + 2, j), dfs(i + 3, m)));
	return dp[i][j];
}

int main()
{
	memset(dp, -1, sizeof(dp));
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%d", a + i);
		a[i] = min(a[i], m);
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		ans = max(ans, dfs(i, m));
	}
	printf("%d\n", ans);
	return 0;
}

还有一种dp写法:

dp[i][j] : 在第i个小时已经吃了j小时,获得的最大价值

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

int n, m;
int a[105], val[105];
int dp[105][105];

int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%d", a + i);
	}
	val[1] = m;
	for(int i = 2; i <= n; i++){
		val[i] = val[i-1] * 2 / 3;
	}
	for(int i = 1; i <= n; i++){
		dp[i][1] = min(a[i], val[1]);
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			if(i >= 1) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + min(val[j], a[i]));
			if(i >= 2) dp[i][j] = max(dp[i][j], dp[i-2][j] + min(val[j], a[i]));
			if(i >= 3) dp[i][1] = max(dp[i][1], dp[i-3][j] + min(val[1], a[i]));
			ans = max(ans, dp[i][j]);
		}
		ans = max(ans, dp[i][1]);
	}
	printf("%d\n", ans);
	return 0;
}

Continuing...

================================================================================

update:

F - Keeping On Track

题目大意:给n条边,然后去掉一个影响最大的点(数据保证有且仅有一个),让你先求出当前没有联通的点的对数,再让你加一条边,求出加了之后最少的没有联通的点的对数。

解题思路:对于第一问,直接枚举删的点,维护最大值并且记录下来

对于第二问,肯定是把最大的两块合起来,一个dfs就可以解决了

身为一个菜鸡,这题妙处真的多,多说几句。

首先,很容易想到如果能够得到每一个节点作为根节点它的子树的大小,就很容易做出来了,但是这样的复杂度是n ^ 2的

那么这样考虑:边dfs边记录、更新答案。

用一个sz数组保存以0为根节点,每个点子树的大小。那么vector里面装它接下来子树的大小,那以前的怎么办呢?很巧妙的用了一个sum记录了dfs下去的和,那么n - sum就是之前的了。那么接下来,如何利用这个vector来判断不连通的点的对数呢?

比如有三个数:1, 2, 3

res = 2 * 1 + 3 * (1 + 2) = 11

验证一下: res = (1 * (2 + 3) + 2 * (1 + 3) + 3 * (1 + 2)) / 2 = 11

这个结论真的太妙了,知道了也就能理解怎么推出来的了

这样,我们只需要一个dfs就能求出枚举所有点被删除的情况了

我真是个图论小白

#pragma GCC diagnostic error "-std=c++11"
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<ext/rope>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e4 + 5;
using namespace std;
using __gnu_cxx::crope;

vector<int> G[maxn];
int sz[maxn];
int n;
ll maxv = 0, id = 0;

void dfs(int u, int fa){
	sz[u] = 1;
	vector<int> vec;
	int sum = 0;
	for(auto v : G[u]){
		if(v == fa) continue;
		dfs(v, u);
		vec.pb(sz[v]);
		sum += sz[v];
		sz[u] += sz[v];
	}
	vec.pb(n - sum);
	ll res = 0;
	ll ss = 0;
	for(auto i : vec){
		res += ss * i;
		ss += i;
	}
	if(res > maxv){
		maxv = res;
		id = u;
	}
}

void dfs2(int u, int fa){
	sz[u] = 1;
	for(int i = 0; i < G[u].size(); i++){
		int v = G[u][i];
		if(v == fa) continue;
		dfs2(v, u);
		sz[u] += sz[v];
	}
}

int main()
{
	FAST;
	cin >> n;
	for(int i = 1; i <= n; i++){
		int u, v; cin >> u >> v;
		G[u].pb(v);
		G[v].pb(u);
	}
	dfs(0, -1);
	dfs2(id, -1);
	vector<ll> vec;
	for(int i = 0; i < G[id].size(); i++) vec.pb(sz[G[id][i]]);
	if(vec.size() == 1) cout << 0 << ' ' << 0 << endl;
	else{
		sort(all(vec));
		vec[vec.size()-2] += vec[vec.size()-1];
		ll ss = 0, res = 0;
		for(int i = 0; i < vec.size() - 1; i++){
			res += ss * vec[i];
			ss += vec[i];
		}
		cout << maxv << ' ' << res << endl;
	}
	return 0;
}

Continuing...

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值