ICPC——2019南京站(A C H J K)

A - A Hard Problem

题意:从 {1,2,⋯,n}中任意选择长度为 k的子集, 保证存在两个数 a, b,a是b的因子。求出最小的 k值。

题解:规律题 k = (n+3)/2
n = 2 3 4 5 6 7 8 9 …
k = 2 3 3 4 4 5 5 6 …

代码

#include<bits/stdc++.h>
using namespace std;

#define endl "\n"
#define ll long long
#define inf 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0); cin.tie(0)
#define debug(a) cout<<"*****\tdebug: "<<a<<"\t*****"<<endl;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 5 + 1e6;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
#endif
	IOS;

// ---------------------------------------------------------------
	int T; cin>>T;
	while(T--)
	{
		int n; cin>>n;
		cout<<(n+3)/2<<endl;
	}
	return 0;
}

C - Digital Path

题意:给定一个 n*m的数字矩阵,从入度为 0的点开始,向上下左右四个方向且增值为 1的方向走,求所有长度大于等于 4的路径数。
答案为 4的示例:

题解:从入度为 0的点向四周搜索,用 dp[i][j][x]代表到点 (i,j)的路径长度为 x的路径数,特别说明 dp[i][j][4]代表路径长度大于等于4的路径数。
递推方程:
dp[xx][yy][k] += dp[x][y][k-1] (2<k<4)
dp[xx][yy][4] += dp[x][y][3] + dp[x][y][4]

代码

#include<bits/stdc++.h>
using namespace std;

#define endl "\n"
#define ll long long
#define inf 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0); cin.tie(0)
#define debug(a) cout<<"*****\tdebug: "<<a<<"\t*****"<<endl;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 5 + 1e3;

int n, m;
int g[N][N], in[N][N], out[N][N], vis[N][N];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int dp[N][N][4];

void dfs()
{
	queue<pair<int, int> > q;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			if(!in[i][j])
			{
				dp[i][j][1] = 1;
				q.push(make_pair(i, j));
			}
		}
	}
	while(q.size())
	{
		int x = q.front().first, y = q.front().second;
		q.pop();
		for(int k = 0; k < 4; k++)
		{
			int xx = x + dx[k], yy = y + dy[k];
			if(xx < 1 || xx > n || yy < 1 || yy > m) continue;
			if(g[x][y]+1 == g[xx][yy])
			{
				(dp[xx][yy][2] += dp[x][y][1]) %= mod;
				(dp[xx][yy][3] += dp[x][y][2]) %= mod;
				(dp[xx][yy][4] += dp[x][y][3] + dp[x][y][4]) %= mod;
				if(--in[xx][yy] == 0) q.push(make_pair(xx, yy));
			}
		}
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
#endif
	IOS;
// ---------------------------------------------------------------
	cin>>n>>m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			cin>>g[i][j];
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			for(int k = 0; k < 4; k++)
			{
				int x = i+dx[k], y = j+dy[k];
				if(x < 1 || x > n || y < 1 || y > m) continue;
				if(g[i][j] == g[x][y]-1) out[i][j]++;
				if(g[i][j] == g[x][y]+1) in[i][j]++;
			}
		}
	}
	dfs();
	int ans = 0;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			if(!out[i][j]) (ans += dp[i][j][4]) %= mod;
		}
	}
	cout<<ans<<endl;
	return 0;
}

H - Prince and Princess

题意:你要在 n个房间中找到公主在哪,每个房间有一个人,他们彼此知道谁在哪个房间。你可以每次问任意一个房间里的人三个问题之一:
1、你的名字是什么?
2、在第 x个房间里的人的名字是什么?
3、公主在哪个房间?

这n个人可以分为三类:一类一定说真话;一类一定说假话;一类可能说真话可能说假话。你知道这三类人的人数分别为 a,b,c,求能否通过问若干个问题,保证一定能找到公主在哪,如果能,输出YES和最少需要的问题数;如果不能,输出NO。

题解:既然要保证能找到,所以就分析最坏的请况:第三类人全说的谎话。然后先问了(b+c)次第三个问题,全是错的(把错的全问完), 然后再问 (b+c+1)次问题, 全是正确的,这样就刚好能确定公主的位置(正确的 > 错误的),这种情况只有在 a > b+c时才可能发生,答案为 2*(b+c)+1。
还有一个重要的点:公主在房间中。所以 a = 1 b = 0 c = 0时,那个人就是公主。

代码

#include<bits/stdc++.h>
using namespace std;

#define endl "\n"
#define ll long long
#define inf 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0); cin.tie(0)
#define debug(a) cout<<"*****\tdebug: "<<a<<"\t*****"<<endl;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 5 + 1e6;



int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
#endif
	IOS;

// ---------------------------------------------------------------
	int a, b, c; cin>>a>>b>>c;
	if(a > b+c) 
	{
		if(a == 1) cout<<"YES"<<endl<<0<<endl;
		else cout<<"YES"<<endl<<2*(b+c)+1<<endl;
	}
	else cout<<"NO"<<endl;
	return 0;
}

J - Spy

题意:你和我要打架,我有一队人 n个,武力值为 a[],赏金为 p[]。你有两队人 2n个,武力值为 b[]和 c[],现在你要把两队人合并为一队(两两配对),新武力值为本来两人之和。每个人只能战斗一次。
你的人打赢我的人,就获得相应的赏金,战斗随机,求你得到的赏金的期望值乘以n

题解:把队伍 b[] 和 c[]当作二部图,边的权值为这对组合能获得的赏金之和。然后跑一遍KM。最佳匹配的边的权值之和即为答案。 n<400, 用O( n 3 n^3 n3)的KM算法

链接 O ( n 3 ) O(n^3) O(n3)的KM模板

代码

#include<bits/stdc++.h>
using namespace std;

#define endl "\n"
#define ll long long
#define inf 0x3f3f3f3f
#define infll 1e15+7
#define IOS ios::sync_with_stdio(0); cin.tie(0)
#define debug(a) cout<<"*****\tdebug: "<<a<<"\t*****"<<endl;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 5 + 400;

ll w[N][N]; 
ll la[N], lb[N], upd[N]; 
bool va[N], vb[N]; 
ll match[N]; 
ll last[N]; 
int n;

ll a[N], p[N], b[N], c[N];

bool dfs(ll x, ll fa) 
{
    va[x] = 1;
    for(int y = 1; y <= n; y++)
    {
		if(!vb[y])
        {
			if(la[x] + lb[y] == w[x][y]) 
			{ 
                vb[y] = 1; last[y] = fa;
                if(!match[y] || dfs(match[y], y)) 
				{
                    match[y] = x;
                    return true;
                }
            }
            else if(upd[y] > la[x] + lb[y] - w[x][y]) 
			{
                upd[y] = la[x] + lb[y] - w[x][y];
                last[y] = fa;
            }
		}
	}
    return false;
}

ll KM() 
{
    for(int i = 1; i <= n; i++) 
	{
        la[i] = -infll;	// !!!
        lb[i] = 0;
        for(int j = 1; j <= n; j++) la[i] = max(la[i], w[i][j]);
    }
    for(int i = 1; i <= n; i++) 
	{
        memset(va, 0, sizeof(va));
        memset(vb, 0, sizeof(vb));
        for(int j = 1; j <= n; j++) upd[j] = infll;	// !!!
       
        int st = 0; match[0] = i;
        while(match[st]) 	
		{ 
            ll delta = infll; // !!!
            if(dfs(match[st], st)) break;
            for(int j = 1; j <= n; j++)
                if(!vb[j] && delta > upd[j]) 
				{
                    delta = upd[j];
                    st = j; 		
                }
            for(int j = 1; j <= n; j++)  
			{
                if(va[j]) la[j] -= delta;
                if(vb[j]) lb[j] += delta; 
				else upd[j] -= delta;
            }
            vb[st] = true;
        }
        while(st)	
		{ 
            match[st] = match[last[st]];
            st = last[st];
        }
    }
	ll ans = 0;
	for(int i = 1; i <= n; i++) 
		if(match[i]) ans += w[match[i]][i];
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
#endif
	IOS;
// ---------------------------------------------------------------
	cin>>n;
	for(int i = 1; i <= n; i++) cin>>a[i];
	for(int i = 1; i <= n; i++) cin>>p[i];
	for(int i = 1; i <= n; i++) cin>>b[i];
	for(int i = 1; i <= n; i++) cin>>c[i];

	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			ll sum = 0;
			for(int k = 1; k <= n; k++)
				if(a[k] < b[i]+c[j]) sum += p[k];
			w[i][j] = sum;
		}
	}
	cout<<KM()<<endl;
	return 0;
}

K - Triangle

题意:给定一个三角形ABC的三个顶点,再给定一个点 p,求另一个点 x的坐标,使得 px的连线平分三角形ABC的面积。假如点 p不在三角形的边上就不合法,输出 -1。

题解:分类讨论 + 二分(也可以通过解方程求)。分类在于,先判断点 p在哪条边上,然后通过点 p离所在边的两个端点的距离大小,判断点 x在对面的哪一条边上。对每一类,二分点 x所在的边,通过面积关系做判断依据,找到点 x的坐标。
注意交版本低一点的,例如G++14能过,但G++20过不了。

代码

#include<bits/stdc++.h>
using namespace std;

#define endl "\n"
#define ll long long
#define inf 0x3f3f3f3f
#define infll 1e15+7
#define IOS ios::sync_with_stdio(0); cin.tie(0)
#define debug(a) cout<<"*****\tdebug: "<<a<<"\t*****"<<endl;
const double eps = 1e-7;
const double pi = acos(-1.0);
const int mod = 1e9+7;
const int N = 5 + 400;

inline double sqr(double x){return x*x;}
int sgn(double x){
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}

struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }
    void input(){
        scanf("%lf%lf",&x,&y);
    }

    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    //叉积
    double operator ^(const Point &b)const{
        return x*b.y - y*b.x;
    }
    //点积
    double operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }

    //返回两点的距离
    double distance(Point p){
        return hypot(x-p.x,y-p.y);
    }
    Point operator +(const Point &b)const{
        return Point(x+b.x,y+b.y);
    }
    Point operator *(const double &k)const{
        return Point(x*k,y*k);
    }

};


struct Line{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s = _s;
        e = _e;
    }

    // 点在线段上的判断
    bool pointonseg(Point p){
        return sgn((p-s)^(e-s)) == 0 && sgn((p-s)*(p-e)) <= 0;
    }
   
};

Point mid_(Point a, Point b)
{
	return (a+b)*0.5;
}

double area(Point a, Point b, Point c)
{
	return fabs((b - a) ^ (c - a) * 0.5);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
#endif
	// IOS;
// ---------------------------------------------------------------
	Point a, b, c ,p;
	Line ab, ac, bc;
	int T; scanf("%d", &T);
	while(T--)
	{
		a.input();
		b.input();
		c.input();
		p.input();
		ab = Line(a, b);
		ac = Line(a, c);
		bc = Line(b, c);
		if(!ab.pointonseg(p) && !bc.pointonseg(p) && !ac.pointonseg(p))
		{
			// cout<<-1<<endl;
			printf("-1\n");
			continue;
		}
		double s = area(a, b, c)/2;
		if(ab.pointonseg(p))
		{
			if(p.distance(a) < p.distance(b))
			{
				Point l = b, r = c;
				Point mid;
				int x = 50;
				while(x--)
				{
					mid = mid_(l, r);
					double ss = area(p, mid, b);
					int flag = sgn(ss-s);
					if(flag == 0) break;
					else if(flag == 1)	r = mid;
					else l = mid;
				}
				printf("%.10lf %.10lf\n", mid.x, mid.y);
			}
			else
			{
				Point l = a, r = c;
				Point mid;
				int x = 50;
				while(x--)
				{
					mid = mid_(l, r);
					double ss = area(p, mid, a);
					int flag = sgn(ss-s);
					if(flag == 0) break;
					else if(flag == 1)	r = mid;
					else l = mid;
				}
				printf("%.10lf %.10lf\n", mid.x, mid.y);
			}
		}
		else if(ac.pointonseg(p))
		{
			if(p.distance(a) < p.distance(c))
			{
				Point l = c, r = b;
				Point mid;
				int x = 50;
				while(x--)
				{
					mid = mid_(l, r);
					double ss = area(p, mid, c);
					int flag = sgn(ss-s);
					if(flag == 0) break;
					else if(flag == 1)	r = mid;
					else l = mid;
				}
				printf("%.10lf %.10lf\n", mid.x, mid.y);
			}
			else
			{
				Point l = a, r = b;
				Point mid;
				int x = 50;
				while(x--)
				{
					mid = mid_(l, r);
					double ss = area(p, mid, a);
					int flag = sgn(ss-s);
					if(flag == 0) break;
					else if(flag == 1)	r = mid;
					else l = mid;
				}
				printf("%.10lf %.10lf\n", mid.x, mid.y);
			}
		}
		else
		{
			if(p.distance(b) < p.distance(c))
			{
				Point l = c, r = a;
				Point mid;
				int x = 50;
				while(x--)
				{
					mid = mid_(l, r);
					double ss = area(p, mid, c);
					int flag = sgn(ss-s);
					if(flag == 0) break;
					else if(flag == 1)	r = mid;
					else l = mid;
				}
				printf("%.10lf %.10lf\n", mid.x, mid.y);
			}
			else
			{
				Point l = b, r = a;
				Point mid;
				int x = 50;
				while(x--)
				{
					mid = mid_(l, r);
					double ss = area(p, mid, b);
					int flag = sgn(ss-s);
					if(flag == 0) break;
					else if(flag == 1)	r = mid;
					else l = mid;
				}
				printf("%.10lf %.10lf\n", mid.x, mid.y);
			}
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值