Live2D

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 3\times 27\times 3\times t\approx 3\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;
}
posted @ 2020-11-23 21:37  Dark_Romance  阅读(103)  评论(0编辑  收藏  举报