1. 程式人生 > >2017CCCC天梯賽決賽 賽後總結

2017CCCC天梯賽決賽 賽後總結

稍微刷了幾道題,所以果然還是做崩了。


賬號hdhd1_hdu0_s3_8  密碼a80d46

拿了252分,總排名算第16名?手速還是不夠,依舊被csy虐慘了233333。

前期開錯了題,先做的L2-004,速度不夠,大概10~20個才做完。

然後發現L3沒人AC,於是立馬轉戰L3-001,做完還是10~20名的樣子。

感覺一開始直接衝L3就好了>.<

接下來把L1水過去了。做L1-6的時候SB了,虧了5分,先回顧下這道題……


https://www.patest.cn/contests/2017gplt-2/L1-6

L1-6. 這道就算難題吧

時間限制 400 ms
記憶體限制 65536 kB
程式碼長度限制 8000 B
判題程式 Standard
作者 翁愷

這裡所謂的“光棍”,並不是指單身汪啦~ 說的是全部由1組成的數字,比如1、11、111、1111等。傳說任何一個光棍都能被一個不以5結尾的奇數整除。比如,111111就可以被13整除。 現在,你的程式要讀入一個整數x,這個整數一定是奇數並且不以5結尾。然後,經過計算,輸出兩個數字:第一個數字s,表示x乘以s是一個光棍,第二個數字n是這個光棍的位數。這樣的解當然不是唯一的,題目要求你輸出最小的解。

提示:一個顯然的辦法是逐漸增加光棍的位數,直到可以整除x為止。但難點在於,s可能是個非常大的數 —— 比如,程式輸入31,那麼就輸出3584229390681和15,因為31乘以3584229390681的結果是111111111111111,一共15個1。

輸入格式:

輸入在一行中給出一個不以5結尾的正奇數x(< 1000)。

輸出格式:

在一行中輸出相應的最小的s和n,其間以1個空格分隔。

輸入樣例:
31
輸出樣例:
3584229390681 15

用Java寫當然很方便啊!結果忘記Java的入口了,就是這個——

public class Main
{
	public static void main(String[] args)
	{
		Scanner cin = new Scanner(System.in);
	}
}

忘記這個怎麼寫了,eclipse試了一會,沒試出來就GG了>.<  沒想到考大數,唉>.<!

然而這道題一個更高效得多的做法是手動模擬高精除單精。比賽的時候懵逼了,自己強行把這個做法ban掉了。有點可惜啊233333
int n;
char ans[N];
int main()
{
	while(~scanf("%d", &n))
	//for(int n = 1; n <= 1000; ++n)if(n % 2 == 1 && n % 10 != 5)
	{
		int p = 0;
		int now = 1;
		for (int len = 1; ; ++len)
		{
			if(p || now / n)ans[p++] = '0' + now / n;
			now %= n;
			if (now == 0)
			{
				ans[p] = 0;
				printf("%s %d\n", ans, len);
				break;
			}
			now = now * 10 + 1;
		}
	}
	return 0;
}

然後【2017CCCC團體程式設計天梯賽】【連結串列模擬】L2-2 重排連結串列

我把while (l <= r) 寫成 while(l < r)了,難過!錯過了3分。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = N * N * 2, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int H, n;
int v[N];
int nxt[N];
int a[N], b[N];

int g;
void dfs(int x)
{
	if (x == -1)return;
	a[++g] = x;
	dfs(nxt[x]);
}
int main()
{
	while (~scanf("%d%d", &H, &n))
	{
		for (int i = 1; i <= n; ++i)
		{
			int x, val, y;
			scanf("%d%d%d", &x, &val, &y);
			v[x] = val;
			nxt[x] = y;
		}

		g = 0; dfs(H);
		int m = 0;
		int r = g;
		int l = 1;
		while (l <= r)
		{
			b[++m] = a[r--];
			if (l > r)break;
			b[++m] = a[l++];
		}b[m + 1] = -1;
		for (int i = 1; i < m; ++i)
		{
			printf("%05d %d %05d\n", b[i], v[b[i]], b[i + 1]);
		}
		int i = m; printf("%05d %d -1\n", b[i], v[b[i]]);
	}
	return 0;
}


然後接下來就是最後2題啦——

【2017CCCC團體程式設計天梯賽】【貪心+線段樹】L3-2 森森快遞

這題直接按照不相覆蓋的區間做貪心,依次列舉,然後使用線段樹更新就好啦~~ 很快就1A了。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define rt 1,1,n
#define ls o<<1
#define rs o<<1|1
#define mid (l+r>>1)
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 4e5 + 10, M = N * N * 2, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
LL flag[N];
LL mn[N];
void pushup(int o)
{
    mn[o] = min(mn[ls], mn[rs]);
}
void pushdown(int o)
{
    if(flag[o])
    {
        mn[ls] -= flag[o]; flag[ls] += flag[o];
        mn[rs] -= flag[o]; flag[rs] += flag[o];
        flag[o] = 0;
    }
}
void build(int o, int l, int r)
{
    flag[o] = 0;
    if(l == r)
    {
        scanf("%lld", &mn[o]);
        return;
    }
    build(lson);
    build(rson);
    pushup(o);
}
struct A
{
    int first, second;
    bool operator < (const A & b)const
    {
        if(first != b.first)return first < b.first;
        return second > b.second;
    }
}a[N], b[N];

LL L, R, V;
LL check(int o, int l, int r)
{
    if(L <= l && r <= R)return mn[o];
    LL ret = 1e18;
    pushdown(o);
    if(L <= mid)gmin(ret, check(lson));
    if(R > mid)gmin(ret, check(rson));
    return ret;
}
void modify(int o, int l, int r)
{
    if(L <= l && r <= R)
    {
        mn[o] -= V;
        flag[o] += V;
        return;
    }
    pushdown(o);
    if(L <= mid)modify(lson);
    if(R > mid)modify(rson);
    pushup(o);
}
int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        --n; build(1, 1, n);
        for(int i = 1; i <= m; ++i)
        {
            int x, y;scanf("%d%d", &x, &y);
            if(x > y)swap(x, y); ++x;
            a[i] = {x, y};
        }
        sort(a + 1, a + m + 1);

        int g = 0;
        for(int i = 1; i <= m; ++i)
        {
            while(g && a[i].first >= b[g].first && a[i].second <= b[g].second)--g;
            b[++g] = a[i];
        }

        LL ans = 0;
        for(int i = 1; i <= g; ++i)
        {
            L = b[i].first;
            R = b[i].second;
            V = check(rt);
            modify(rt);
            ans += V;
        }
        printf("%lld\n", ans);
    }
    return 0;
}


最後一題實在是因為時間不多了。其實就是很基礎的最短路。所以說也許拼一拼時間,爭取多A個題,還是很有必要的。

L3-3. 森森美圖

時間限制 400 ms
記憶體限制 65536 kB
程式碼長度限制 8000 B
判題程式 Standard 作者 戴龍翱、朱建科

森森最近想讓自己的朋友圈熠熠生輝,所以他決定自己寫個美化照片的軟體,並起名為森森美圖。眾所周知,在合照中美化自己的面部而不美化合照者的面部是讓自己佔據朋友圈高點的絕好方法,因此森森美圖裡當然得有這個功能。 這個功能的第一步是將自己的面部選中。森森首先計算出了一個影象中所有畫素點與周圍點的相似程度的分數,分數越低表示某個畫素點越“像”一個輪廓邊緣上的點。 森森認為,任意連續畫素點的得分之和越低,表示它們組成的曲線和輪廓邊緣的重合程度越高。為了選擇出一個完整的面部,森森決定讓使用者選擇面部上的兩個畫素點A和B,則連線這兩個點的直線就將影象分為兩部分,然後在這兩部分中分別尋找一條從A到B且與輪廓重合程度最高的曲線,就可以拼出使用者的面部了。 然而森森計算出來得分矩陣後,突然發現自己不知道怎麼找到這兩條曲線了,你能幫森森當上朋友圈的小王子嗎?

為了解題方便,我們做出以下補充說明:

  • 影象的左上角是座標原點(0,0),我們假設所有畫素按矩陣格式排列,其座標均為非負整數(即橫軸向右為正,縱軸向下為正)。
  • 忽略正好位於連線A和B的直線(注意不是線段)上的畫素點,即不認為這部分畫素點在任何一個劃分部分上,因此曲線也不能經過這部分畫素點。
  • 曲線是八連通的(即任一畫素點可與其周圍的8個畫素連通),但為了計算準確,某畫素連線對角相鄰的斜向畫素時,得分額外增加兩個畫素分數和的根號2倍減一。例如樣例中,經過座標為(3,1)和(4,2)的兩個畫素點的曲線,其得分應該是這兩個畫素點的分數和(2+2),再加上額外的(2+2)乘於(根號2減一),即約為5.66。

輸入格式:

輸入在第一行給出兩個正整數N和M(5 <= N, M <= 100),表示畫素得分矩陣的行數和列數。

接下來N行,每行M個不大於1000的非負整數,即為畫素點的分值。

最後一行給出使用者選擇的起始和結束畫素點的座標(Xstart, Ystart)和(Xend, Yend)。4個整數用空格分隔。

輸出格式:

在一行中輸出劃分圖片後找到的輪廓曲線的得分和,保留小數點後兩位。注意起點和終點的得分不要重複計算。

輸入樣例:
6 6
9 0 1 9 9 9
9 9 1 2 2 9
9 9 2 0 2 9
9 9 1 1 2 9
9 9 3 3 1 1
9 9 9 9 9 9
2 1 5 4
輸出樣例:
27.04

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 105, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
int a[N][N];

struct Point
{
	int y, x;
	bool operator != (const Point &b)const
	{
		return x != b.x || y != b.y;
	}
}p0, p1;

//基礎函式,判定p2相對於(p0->p1)的方向:①正,p2在p1的逆時針向;②負,則p2在p1的順時針向;③零,共線
LL cp(Point p0, Point p1, Point p2)
{
	LL x1 = p1.x - p0.x;
	LL y1 = p1.y - p0.y;
	LL x2 = p2.x - p0.x;
	LL y2 = p2.y - p0.y;
	return x1 * y2 - x2 * y1;
}

struct Node
{
	Point p; double dis;
	bool operator < (const Node &b) const
	{
		return dis > b.dis;
	}
};

priority_queue<Node>q;
double f[N][N];
bool e[N][N];
bool DIR;
void inq(Point p, double dis)
{
	if (p.y < 1 || p.y > n || p.x < 1 || p.x > m)return;
	if (dis >= f[p.y][p.x])return;
	
	if (DIR)
	{
		if (p != p0 && p != p1 && cp(p0, p1, p) <= 0)return;
	}
	else
	{
		if (p != p0 && p != p1 && cp(p0, p1, p) >= 0)return;
	}

	f[p.y][p.x] = dis;
	if (!(p != p1))return;
	q.push({ p, dis });
}

const int dy[4] = { -1, 0, 0 ,1 };
const int dx[4] = { 0, -1, 1, 0 };
const int py[4] = { -1,-1,1,1 };
const int px[4] = { -1,1,-1,1 };
void bfs()
{
	while (!q.empty())q.pop();
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 1; j <= m; ++j)
		{
			f[i][j] = inf;
			e[i][j] = 0;
		}
	}
	inq(p0, a[p0.y][p0.x]);
	while (!q.empty())
	{
		Point now = q.top().p; q.pop();
		if (e[now.y][now.x])continue; e[now.y][now.x] = 1;
		for (int k = 0; k < 4; ++k)
		{
			int y = now.y + dy[k];
			int x = now.x + dx[k];
			inq({ y, x }, f[now.y][now.x] + a[y][x]);
		}
		for (int k = 0; k < 4; ++k)
		{
			int y = now.y + py[k];
			int x = now.x + px[k];
			inq({ y, x }, f[now.y][now.x] + a[y][x] + (a[y][x] + a[now.y][now.x]) * (sqrt(2) - 1));
		}
	}
}
int main()
{
	while(~scanf("%d%d", &n, &m))
	{
		for (int i = 1; i <= n; ++i)
		{
			for (int j = 1; j <= m; ++j)
			{
				scanf("%d", &a[i][j]);
			}
		}
		scanf("%d%d%d%d", &p0.x, &p0.y, &p1.x, &p1.y);
		++p0.x; ++p0.y; ++p1.x; ++p1.y;

		double ans = 0;
		DIR = 0; bfs(); ans += f[p1.y][p1.x];
		DIR = 1; bfs(); ans += f[p1.y][p1.x];
		ans -= a[p0.y][p0.x];
		ans -= a[p1.y][p1.x];
		printf("%.2f\n", ans);
	}
	return 0;
}
/*
【題意】
其實就是輪廓線的點權和儘可能小,如果斜著走,從x走到y的話,會額外增加(v[x] + v[y]) * (sqrt(2) - 1)

【分析】
用叉積分路線,求最短路即可。

【資料】
9 0 1 9 9 9
9 9 1 2 2 9
9 9 2 0 2 9
9 9 1 1 2 9
9 9 3 3 1 1
9 9 9 9 9 9
(3 2) (6 5)
1 2 2 9
2 0 2 9
1 1 2 9
3 3 1 1

答案 = 27.04 = 3 + 17*(2^0.5)
4sqrt(2) 11sqrt(2) 2sqrt(2)
1 2 1 1 1 1
2 2 9
= 20 + 17sqrt(2) - 17

*/