GDUFS 2019信息学院程序设计新手赛(正式赛)题解

题目连接 :https://oj.lpoj.cn/contest/56

A lpoj is the best

这题是送分题,主要考察转义字符

#include<iostream>
using namespace std;
int main(){
  
  int n;
  cin>>n;
  while(n--){
  cout<<"\"www.lpoj.cn is the best!\""<<endl;
    
  }
 	return 0; 
}

B 偶数求和

考察int的范围,这题要用long long int长整型才能保存答案

#include<iostream>
using namespace std;

int main()
{
	int n,i;
	long long int tmp,s=0;
	cin >>n;
	for (i=0;i<n;i++) 
	{
		cin >>tmp;
		if (tmp%2==0) { s+=tmp; }
	}
	cout <<s;
	return 0;
	
}

C 出题人的良心

主要考察大家的读题和细心能力。这道题用printf的话,会简单很多,少很多逻辑。

#include <stdio.h>
int main()
{
	int a,b;
	float c;
	scanf("%d %d",&a,&b);
	c=(float)a/b;
	if(b>0)
	printf("%d/%d=%.2f\n",a,b,c);
	if(b<0)
	printf("%d/(%d)=%.2f\n",a,b,c);
	if(b==0)
	printf("%d/%d=Error\n",a,b);
	return 0;	
}

D cat & grep

这题是出题人拍脑袋想出来的。一个for循环就好了。

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

int n;
char s[1100];

int main(){
    while(~scanf("%s", s)){
        n = strlen(s);
        int ans = 0;
        for(int i=3; i<n; i++){
            if(s[i-3] == 'l' && s[i-2] == 'p' && s[i-1] == 'o' && s[i] == 'j'){
                ans++;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

E ka ge bu n shi n no jyu tsu

简单思维题,数字优先拆3,接着拆2,至于为什么,因为要使乘积最大,肯定是越平均越好,直到3,因为3不能拆了。

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

int n, m;

bool ok(){
    if(n >= 60) return 1;
    long long s = 1;
    while(n > 4){
        s = s * 3;
        n -= 3;
        if(s >= m)  return 1;
    }
    return s * n >= m;
}

int main(){
    while(~scanf("%d %d", &n, &m)){
        puts(ok()?"Yes":"No");
    }
    return 0;
}

F 好快的刀

做法就是因为数据范围很小,所以我们可以枚举所有直线,然后计算所有圆到直线的距离即可。但是这里有很多特殊情况要考虑。比如 斜率不存在的情况等等。

#include <iostream>
#include <cmath>
using namespace std;
const double eps=1e-8;
const double inf =1e20;
//const double pi=acos(-1.0);
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);
    }
    bool operator ==(Point b)const{
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }
    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 distance(Point p){
        return hypot(x-p.x,y-p.y);
    }
};
struct Line{
    Point s,e;
    void input(){
        s.input();
        e.input();
    }
    double length(){
        return s.distance(e);
    }
    double dispointtoline(Point p){
        return fabs((p-s)^(e-s))/length();
    }
};
struct circle{
    Point p;
    double r;
    void input(){
        p.input();
        scanf("%lf",&r);
    }
    bool operator ==(circle v){
        return (p==v.p);
    }
    int relationline(Line v){
        double dst=v.dispointtoline(p);
        if(sgn(dst-r)<0) return 2;
        else if(sgn(dst-r)==0) return 1;
        else return 0;
    }
    
};
int main(){
    int n,ans(2),cou,flag(0);
    circle cir[105];
    Line line1;
    cin>>n;
    for(int i=0;i<n;i++) cir[i].input();
    for(int i=0;i<n;i++){
        if(cir[i]==cir[0]&&i==n-1)flag=1;
    }
    if(flag){
        cout<<n<<'\n';
        return 0;
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cou=2;
            if(i==j) continue;
            line1.e=cir[i].p;
            line1.s=cir[j].p;
            for(int k=0;k<n;k++){
                if(k==i||k==j) continue;
                if(cir[k].relationline(line1)!=0) cou++;
            }
            ans=max(ans,cou);
        }
    }
    cout<<ans<<'\n';
    return 0;
}

G 沙雕

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,m,w,h; 
int p1,p2;
int main(){
	cin >> n >> m >> w >> h;
	for (int i=w+2;i<=n;i++)
		p1=(p1+1ll*(i-w-1)*(n-i+1)%mod)%mod;
	for (int i=h+2;i<=m;i++)
		p2=(p2+1ll*(i-h-1)*(m-i+1)%mod)%mod;
	cout << 1ll*p1*p2%mod;
}

H 完美子矩阵

首先因为行内可以随意变换,所以只要直接统计0-9数字的每一行是否符合回文的条件。然后,N * M就压缩成N * 10的东西了。因为要满足行回文,因此只要每一行0-9对应的数量一样,并且每一行都合法就是一个回文子矩阵。因为,问题就转化为给你一个序列,问你回文的子串有多少个。
O(N^3)做法:O( n^2)枚举左右端点,暴力O(n)检测,这个明显时间过不了
O(N^2)做法:O(n)枚举回文的对称点,O(n)检测该回文对称的最长半径
O(N)做法:直接用Manacher算法

#include <bits/stdc++.h>

using namespace std;
int n, m;
vector<int> num[2005];
bool ok[2005];

bool check1(int r)
{
    int sum = 0;
    for (auto &&i:num[r])
        if (i & 1)
            ++sum;
    return sum < 2;
}

int main()
{
    string t;
    cin >> n >> m;
    for (int i = 0; i < n; ++i)
        num[i] = vector<int>(10);
    for (int i = 0; i < n; i++)
    {
        cin >> t;
        for (auto &&j:t)
            ++num[i][j - 0x30];
        ok[i] = check1(i);
    }
    int l, r, ans = 0;
    for (int i = 0; i < n; ++i)
    {
        l = r = i;
        while (l >= 0 && r < n)
        {
            if (ok[l] && ok[r] && num[l] == num[r])
                ++ans;
            else
                break;

            --l, ++r;
        }
        l = i, r = i + 1;
        while (l >= 0 && r < n)
        {
            if (ok[l] && ok[r] && num[l] == num[r])
                ++ans;
            else
                break;
            --l, ++r;
        }
    }
    cout << ans;
    return 0;
}

I 斐波那契通项

暴力:可以发现这个通项是两项线性递推的公式,double暴力算出前5项,暴力枚举两项系数即可,也可以解二元一次方程组。
科学做法:用特征根来求解,可以百度特征方程求解斐波那契通项

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

typedef long long LL;

const int N = 1e5 + 10;
int n, mod;
LL a, b, F[N];

int main(){
    scanf("%lld %lld %d %d", &a, &b, &n, &mod);
    LL A = (a * 2) % mod;
    LL B = ((b - a * a) % mod + mod) % mod;
    F[0] = 2;
    F[1] = A;
    for(int i=2; i<=n; i++){
        F[i] = (F[i-1] * A + F[i-2] * B) % mod;
    }
    printf("%lld\n", F[n]);
    return 0;
}

J 摇摇摇

概率动态规划,令fi表示在i时走到1的概率期望,其中f1=0,通过解方程,f2=f3=f4=f5=f6=6,接下来就是一个线性递推fi = 1/6*(fi-1+fi-2+fi-3+fi-4+fi-5+fi*6)+1。

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

const int N = 1e5 + 10;

int n;
double dp[N];

int main(){
    dp[1] = 0.0;
    for(int i=2; i<=6; i++){
        dp[i] = 6.0;
    }
    for(int i=7; i<N; i++){
        dp[i] = 1.0;
        for(int j=1; j<=6; j++){
            dp[i] = dp[i] + dp[i-j] / 6.0;
        }
    }

    while(~scanf("%d", &n)){
        printf("%.3f\n", dp[n]);
    }
    return 0;
}

K Hongrock的柠檬树

引入某位集训队大佬的题解:

输入一棵树,求树上所有两点之间路径或之和。

权神设计出来是用并查集做的。

然而第一反应就是向上合并维护二进制位。写了一发维护1的个数愉快的wa了,之后就忘了有这题了。最近这题被大师兄秒掉了,被教育了一手可以先当成整棵树全是1来做,然后再合并0的个数,减掉0的情况。

瞬间好写了很多。。。。

我们把子树0的个数向上合并的时候,检查一下父节点这一位是否是0,如果是0才合并,因为父节点是所以子节点的必经之路,如果父节点是1,那么子节点经过父节点往上或出来这一位就是1了。

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 5;
typedef long long ll;

ll ans;
int n, val[maxn];
int p[20], sz[maxn][20];
vector<int> G[maxn];


void crack(int x) {
    for (int i = 0; i < 20; ++i) {
        sz[x][i] = !(val[x] >> i & 1);
    }
}

void dfs(int x, int fa) {
    crack(x);

    for (auto v:G[x]) {
        if (v == fa) {
            continue;
        }
        dfs(v, x);
        for (int i = 0; i < 20; ++i) {
            ans -= 1LL * p[i] * sz[x][i] * sz[v][i];
            if (!(val[x] >> i & 1)) {
                sz[x][i] += sz[v][i];
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> val[i];
    }

    int u, v;
    for (int i = 1; i < n; ++i) {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    p[0] = 1;
    for (int i = 1; i < 20; ++i) {
        p[i] = p[i - 1] << 1;
    }

    for (int i = 0; i < 20; ++i) {
        ans += 1LL * p[i] * n * (n - 1) / 2;
    }

    dfs(1, 0);
    cout << ans;
    return 0;
}

L Alice and Bob

初始化部分,对两棵树分别做深搜,维护好LCA(最近公共祖先)所需信息,查一棵树上任意两点间的距离。
看询问,虽然说是两个人的相遇问题,但由于也是两个人的距离之和,所以可以看成一个点到另一个点的最短距离。
传送门只有M个,那么在每个询问中,我们只需要关注这M对点的路径。
比赛时有同学已经做到了LCA了,只是他是通过枚举M对点到两个点的距离之和求最小值,也就是说,在这个策略里,传送门只利用一次。
但是实际上,传送门可以多次利用,在两棵树之间来回跳来求出更短的距离,所以这里的子问题应该是,起点到这2M个点的最短路径,跑个单源最短路算法即可。

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

const int N = 2e5 + 10;
const int L = 20;
const int M = 8;
typedef long long LL;
typedef pair<int,int> pii;
const LL inf = 1e18;

#define pb push_back
#define mp make_pair

int n, Q, m, a[M], dep[N], fa[N][L];
LL dist[N];
vector<pii> V[N];

void dfs(int x){
	for(int i=0; i<V[x].size(); i++){
		int j = V[x][i].first;
		if(j == fa[x][0])	continue;
		dist[j] = dist[x] + V[x][i].second;
		dep[j] = dep[x] + 1;
		fa[j][0] = x;
		dfs(j);
	}
}

void init(){
	dep[1] = 0;
	fa[1][0] = -1;
	dist[1] = 0;
	dfs(1);
	
	dep[n+1] = 0;
	fa[n+1][0] = -1;
	dist[n+1] = 0;
	dfs(n+1);

	for(int j=1; j<L; j++){
		for(int i=1; i<=n*2; i++){
			if(fa[i][j-1] == -1){
				fa[i][j] = fa[i][j-1];
			} else {
				fa[i][j] = fa[fa[i][j-1]][j-1];
			}
		}
	}
}

bool done[M];
LL d[M];
struct Node{
	int id;
	LL dis;
	Node(){}
	Node(int id, LL dis):id(id),dis(dis){}
	bool operator < (const Node &A)const{
		return dis > A.dis;
	}
};

int lca(int u, int v){
	if(dep[u] > dep[v])	swap(u, v);
	for(int i=L-1; i>=0; i--){
		if(dep[v] - dep[u] >> i & 1){
			v = fa[v][i];
		}
	}
	if(u == v)	return u;
	for(int i=L-1; i>=0; i--){
		if(fa[u][i] != fa[v][i]){
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}

LL cal(int x, int y){
	int u = lca(x, y);
	return dist[x] + dist[y] - dist[u] * 2;
}

LL solve(int x, int y, int m){
	LL ans = inf;

	priority_queue<Node> Q;
	Node nd;
	LL g;

	for(int i=0; i<m; i++){
		d[i] = cal(x, a[i]);
		Q.push(Node(i, d[i]));
		done[i] = 0;
	}

	while(!Q.empty()){
		nd = Q.top(); Q.pop();
		if(done[nd.id])	continue;
		if(nd.dis >= ans)	break;

		ans = min(ans, nd.dis + cal(a[nd.id]+n, y));

		done[nd.id] = 1;
		
		for(int i=0; i<m; i++){
			if(done[i])	continue;
			g = min(cal(a[i], a[nd.id]), cal(a[i]+n, a[nd.id]+n));
			if(d[i] > nd.dis + g){
				d[i] = nd.dis + g;
				Q.push(Node(i, d[i]));
			}
		}
	}
	
	return ans;
}

int main(){
	int x, y, z, m;

	scanf("%d", &n);
	
	for(int i=1; i<n; i++){
		scanf("%d %d %d", &x, &y, &z);
		V[x].pb(mp(y, z));
		V[y].pb(mp(x, z));
	}
	
	for(int i=1; i<n; i++){
		scanf("%d %d %d", &x, &y, &z);
		V[x].pb(mp(y, z));
		V[y].pb(mp(x, z));
	}

	init();

	scanf("%d", &Q);
	while(Q--){
		scanf("%d %d %d", &x, &y, &m);
		for(int i=0; i<m; i++){
			scanf("%d", a+i);
		}
		printf("%lld\n", solve(x, y, m));
	}

	return 0;
}
发布了402 篇原创文章 · 获赞 67 · 访问量 16万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 点我我会动 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览