1. 程式人生 > 實用技巧 >2020-11-23 考試總結

2020-11-23 考試總結

本來考得多好的,結果因為自己犯了低階錯誤,掛了145,於是285->140。。。

T1 組合

題目傳送門

思路

不難看出這個題目就是讓你找尤拉路徑,然而我一直以為這是一個 NP 問題,於是考場果斷爆搜,結果 YES/NO 打成 Yes/No,掛了40分。

講正解,不難發現如果我們認定這玩意一定可以,那麼只要我們找對起點,隨便亂搜都可以做到線性。(可以類似於當前弧優化)

考慮如何判定,對於有向圖,肯定入度與出度不一樣的點不能超過2個,因為只有放在起點和終點,其餘的點只要進去就會出來一次。而且入度與出度相差不能大於1,原因相同。

對於無向圖,度數為奇數的點不能超過2個,證明類似。

選起點的話可以按上面的條件選。時間複雜度可以做到 \(O(V+E)\)

\(\texttt{Code}\)

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

#define Int register int
#define MAXM 400005
#define MAXN 100005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int T,n,m;

struct edge{
	int v,w,nxt;
	edge(){}
	edge (int _v,int _w,int _nxt){v = _v,w = _w,nxt = _nxt;}
}e[MAXM];
int fir,top,toop = 1,fa[MAXN],sta[MAXM],head[MAXN];
int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);}

void add_edge (int u,int v,int w){
	e[++ toop] = edge (v,w,head[u]);
	head[u] = toop;
}

bool vis[MAXM];
void dfs (int u){
	for (Int i = head[u];i;){
		if (vis[i >> 1]){
			head[u] = e[i].nxt;
			i = e[i].nxt;
			continue;
		}
		vis[i >> 1] = 1,dfs (e[i].v),sta[++ top] = e[i].w,i = head[u];
	}
}

int ind[MAXN],outd[MAXN],deg[MAXN];

int Fabs (int x){return x < 0 ? -x : x;}
signed main(){
	read (T),read (n),read (m);
	for (Int i = 1;i <= n;++ i) fa[i] = i;
	for (Int i = 1,u,v;i <= m;++ i){
		read (u),read (v);
		fa[findSet (u)] = findSet (v);
		if (T == 1) add_edge (u,v,i),add_edge (v,u,-i),deg[u] ++,deg[v] ++;
		else add_edge (u,v,i),add_edge (0,0,0),ind[v] ++,outd[u] ++;
	}
	for (Int i = 1;i <= n;++ i) if (findSet (i) != findSet (1) && deg[i]) return puts ("NO"),0;
	if (T == 1){
		int cnt = 0;
		for (Int i = 1;i <= n;++ i) if (deg[i] & 1) cnt ++,fir = i;
		if (cnt > 2) return puts ("NO"),0;
		for (Int i = 1;i <= n && !fir;++ i) if (deg[i]) fir = i;
		dfs (fir);
		puts ("YES");
		for (Int i = m;i >= 1;-- i) write (sta[i]),putchar (' ');putchar ('\n');
	}
	else{
		int cnt = 0;
		for (Int i = 1;i <= n;++ i) if (ind[i] ^ outd[i]){
			cnt ++;
			if (outd[i] > ind[i]) fir = i;
			if (Fabs (outd[i] - ind[i]) > 1) return puts ("NO"),0;
		}
		if (cnt > 2) return puts ("NO"),0;
		for (Int i = 1;i <= n && !fir;++ i) if (outd[i]) fir = i;
		dfs (fir),puts ("YES");
		for (Int i = m;i >= 1;-- i) write (sta[i]),putchar (' ');putchar ('\n');
	}
	return 0;
}

T2 小 W 的魔術

題目傳送門

思路

水題,考場過了,結果因為測評機子太慢被卡掉了 60 分。

T3 小 Y 的圖

題目傳送門

思路

水題,沒有什麼好說的。

T4 小 L 的數

題目傳送門

思路

不難看出答案一定不會大於 \(4\),因為可以用 \(1,2,4,8\) 來構造。

考慮怎麼判斷用 3 個及以內是否可以構成。可以想到數位 dp,我們可以設 \(f_{i,j,0/1,0/1,0/1}\) 表示當前第 \(i\) 位,進位為 \(j\),每個數是否進入前導零。

然後你發現你這個有點卡。然後你發現實際上在轉移的時候可以列舉進了多少位,然後時間複雜度就下去了。

時間複雜度大概是 \(\binom{45}{3}\log n\times 2\times 27\times 2\times t\approx 1.5\times 10^8\)

,可能卡得過去吧。。。

\(\texttt{Code}\)

#pragma GCC optmize("O2")
#include <bits/stdc++.h>
using namespace std;

#define y1 fuckyourwholefamily 
#define Int register int
#define ll long long

char buf[1<<20],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
template <typename T> void read (T &x){char c = getchar ();x = 0;while (!isdigit (c)) c = getchar ();while (isdigit(c)) x = x * 10 + c - '0',c = getchar ();}
template <typename T> void write (T x){if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int len,num[21],used[3][2];
bool dp[21][3][8],init[8][30];

inline void getinit (){
	memset (init,0,sizeof (init));
	for (Int S = 0;S < (1 << 3);++ S)
		for (Int i = 0;i <= 1;++ i)
			for (Int j = 0;j <= 1;++ j)
				for (Int k = 0;k <= 1;++ k)
					init[S][(S >> 2 & 1) * used[0][i] + (S >> 1 & 1) * used[1][j] + (S & 1) * used[2][k]] = 1;
}

inline bool Work(){
	memset (dp,0,sizeof (dp)),dp[1][0][7] = 1;
	for (Int now = 1;now <= len;++ now){
		bool fid = 0;
		for (Int add = 0;add <= 2;++ add)
			for (Int S = 0;S < 8;++ S){
 				if (!dp[now][add][S]) continue;
				else{
					fid |= 1;
					for (Int i = 0;i <= 2;++ i){
						int ned = num[now] - add;if (ned < 0) ned += 10;
						if (init[S][i * 10 + ned]){
							int get = i * 10 + ned + add;dp[now + 1][get / 10][0] = 1;
							for (Int T = S;T;T = (T - 1) & S) dp[now + 1][get / 10][T] = 1;
						}
					}
				}
			}
		if (!fid) return 0;
	}
	return dp[len + 1][0][0];
}

bool visit[10];

signed main(){
	int t;read (t);
	while (t --> 0){
		len = 0;int tot = 0;
		ll x;read (x);
		memset (visit,0,sizeof (visit));
		while (x) tot += !visit[x % 10],visit[x % 10] = 1,num[++ len] = x % 10,x /= 10;
		if (tot <= 2) puts ("1");
		else{
	 		int ans = 4;
			for (used[0][0] = 0;used[0][0] <= 9 && ans != 2;++ used[0][0])
				for (used[0][1] = used[0][0];used[0][1] <= 9 && ans != 2;++ used[0][1])
					for (used[1][0] = used[0][0];used[1][0] <= 9 && ans != 2;++ used[1][0])
						for (used[1][1] = used[1][0];used[1][1] <= 9 && ans != 2;++ used[1][1])
							for (used[2][0] = used[1][0];used[2][0] <= 9 && ans != 2;++ used[2][0])
								for (used[2][1] = used[2][0];used[2][1] <= 9 && ans != 2;++ used[2][1]){
								bool c1 = used[0][0] == 0 && used[0][1] == 0,c2 = used[1][0] == 0 && used[1][1] == 0,c3 = used[2][0] == 0 && used[2][1] == 0;
								if (3 - c1 - c2 - c3 < ans){
									getinit ();
									bool chk = 0;for (Int i = 0;i <= 2;++ i) chk |= init[7][i * 10 + num[1]];
									if (chk && Work()) ans = 3 - c1 - c2 - c3;
								} 
							}
			write (ans),putchar ('\n');
		}
	}
	return 0;
}