【比赛】USACO21 Jan

【比赛】USACO21 Jan

文章目录

  • 【比赛】USACO21 Jan
    • [P7296 [USACO21JAN] Uddered but not Herd G](https://www.luogu.com.cn/problem/P7296)
    • [P7297 [USACO21JAN] Telephone G](https://www.luogu.com.cn/problem/P7297)
    • [P7298 [USACO21JAN] Dance Mooves G](https://www.luogu.com.cn/problem/P7298)
    • 碎碎念

Dec的时候进了Gold,于是这次只参加了Gold

P7296 [USACO21JAN] Uddered but not Herd G

题意:
给定字符串s,试构造一种小写字母排列p,使得将s划分为若干组,每组字符串均为p的子序列,且使得组数最少,输出最少组数。

保证s长度小于等于1e5且s出现的不同字符不超过二十个

题解:

因为 ∣ p ∣ |p| p很小,可以考虑状压DP

正难则反,考虑把每个字符划分为一段

如果相邻两个字母在排列中顺序就把他们合并起来

d p [ i ] dp[i] dp[i]表示当前的排列中已经选择了的字母集合

更新时枚举下一个字母的选择

对于在排列中出现过的字母如果在原串中位于当前字母之后

则他们无法合并,产生对答案的贡献

可以先处理出每一对相邻字母数量

记忆化搜索即可

#include
using namespace std;
inline int Read(){
  	int s = 0 , w = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0'){
		if(ch == '-') w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		s = (s << 1) + (s << 3) + ch - '0';
		ch = getchar();
	}
	return s * w;
}
const int MAXN = 1e5 + 50;
char s[MAXN];
int a[MAXN];
int p[25][25];
int n;
int id[300],tot = 0;
int f[(1 << 21) + 10];
int DP(int ss){
	if(ss == (1 << tot) - 1) return 0;
	if(f[ss]) return f[ss];
	int res = 0;
	for(int i = 1 ; i <= tot ; i ++){
		if((ss >> (i - 1)) & 1) continue;
		int tmp = 0;
		for(int j = 1 ; j <= tot ; j ++){
			if((ss >> (j - 1)) & 1){
				tmp += p[j][i];
			}
		}
		res = max(res,tmp + DP((1 << (i - 1)) | ss));
	}
	return (f[ss] = res);
}
int main(){
	scanf("%s",s + 1);
	n = strlen(s + 1);
	for(int i = 1 ; i <= n ; i ++){
		if(!id[s[i]]) id[s[i]] = ++ tot;
		a[i] = id[s[i]];
	}
	for(int i = 1 ; i + 1 <= n ; i ++){
		p[a[i]][a[i + 1]] ++;
	}
	int ans = 0x3f3f3f3f;
	for(int i = 1 ; i <= 20 ; i ++){
		ans = min(ans,n - DP(1 << (i - 1)));
	}
	cout << ans << endl;
 	return 0;
}

P7297 [USACO21JAN] Telephone G

题意:
给定n个点,每个点被分配k种颜色其中一种,任意两点 i , j i,j i,j距离定义为 ∣ i − j ∣ |i-j| ij,每种颜色的点向某些特定颜色的点连边(给定k*k个关系),求1到n的路径最小距离

颜色数量不多于20,n不多于5e4

题解:
这题应该是整场最easy的

看k的数据范围和求最短路就知道要建立分层图

还是比较明显的

我们按照以下步骤建图:

1.将每个点拆成k+1个点,k个点对应k个颜色,1个是本身

2.将k*n个点中每相同颜色的n个点相连,共连k层,双向边,边权为1

3.将每个点连接当前点颜色可到达的颜色对应的当前点拆出来的点,也将可到达当前点颜色的颜色对应的当前点拆出来的点连接当前点,边权均为0,单向边

最后跑从1到n的最短路即可

#include
using namespace std;
inline int Read(){
  	int s = 0 , w = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0'){
		if(ch == '-') w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		s = (s << 1) + (s << 3) + ch - '0';
		ch = getchar();
	}
	return s * w;
}
const int MAXN = 60 * (5e4 + 50);
int n,k;
int a[MAXN],s[55][55];
int d[MAXN];
bool vis[MAXN];
priority_queue<pair<int,int> >Q;
int head[MAXN],to[MAXN << 1],nxt[MAXN << 1],val[MAXN << 1],tot;
void add(int x,int y,int v){
	to[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
	val[tot] = v;
}
int id(int i,int j){
	return i * n + j;
}
void Dijkstra(int s){
	memset(d,127,sizeof(d));
	Q.push(make_pair(0,s));
	d[s] = 0;
	while(!Q.empty()){
		int u = Q.top().second;
		Q.pop();	
		if(vis[u]) continue;
		vis[u] = true;
		for(int i = head[u] ; i ; i = nxt[i]){
			int v = to[i];
			if(d[v] > d[u] + val[i]){
				d[v] = d[u] + val[i];
				Q.push(make_pair(-d[v],v));
			}
		}
 	}
}
int main(){
	n = Read() , k = Read();
	for(int i = 1 ; i <= n ; i ++) a[i] = Read();
	for(int i = 1 ; i <= k ; i ++){
		for(int j = 1 ;  j <= k ; j ++){
			char ch = getchar();
			while(ch != '0' && ch != '1') ch = getchar();
			s[i][j] = ch - '0';
		}
	}
	for(int i = 1 ; i <= k ; i ++){
		for(int j = 1 ; j + 1 <= n ; j ++){
			add(id(i,j),id(i,j + 1),1);
			add(id(i,j + 1),id(i,j),1);
		}
	}
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= k ; j ++){
			if(s[a[i]][j]) add(i,id(j,i),0);
		}
		add(id(a[i],i),i,0);
	}
	Dijkstra(1);
	if(d[n] < 0x7f7f7f7f) printf("%d\n",d[n]);
	else printf("-1\n");
	return 0;
}

P7298 [USACO21JAN] Dance Mooves G

这题就只打了两个部分分一共是50%的

题解就不写了(咕咕咕)

等学会正解再来

u1s1这题是真的毒瘤

#include
using namespace std;
#define int long long
inline int Read(){
  	int s = 0 , w = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0'){
		if(ch == '-') w = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		s = (s << 1) + (s << 3) + ch - '0';
		ch = getchar();
	}
	return s * w;
}
const int MAXN = 1e5 + 50;
int sp[MAXN << 1][2];
int n,m,k;
namespace A{
	const int N = 100 + 5;
	vector<int>vis[N],t[N];
	int to[N],a[N];
	bool wtb[N];
	bitset<N>plc;
	int res[N];
	void solve(){
		for(int i = 1 ; i <= n ; i ++){
			a[i] = i;
			vis[i].push_back(i);
			t[i].push_back(0);
		}
		for(int i = 1 ; i <= k ; i ++){
			vis[a[sp[i][0]]].push_back(sp[i][1]);
			vis[a[sp[i][1]]].push_back(sp[i][0]);
			t[a[sp[i][0]]].push_back(i);
			t[a[sp[i][1]]].push_back(i);
			swap(a[sp[i][0]],a[sp[i][1]]);
		}
		for(int i = 1 ; i <= n ; i ++){
			to[a[i]] = i;
		}
		for(int i = 1 ; i <= n ; i ++){
			memset(wtb,false,sizeof(wtb));
			plc.reset();
			int now = i;
		//	cout << i << ":" << endl;
			for(int j = 0 ; j <= m && !wtb[now] ; now = to[now]){
				if(wtb[now]) break;
				wtb[now] = true;
		//		cout << now << endl;
				if(j + k <= m){
					for(int q = 0 ; q < vis[now].size() ; q ++){
						plc[vis[now][q]] = 1;
					}
					j += k;
				}
				else{
					for(int q = 0 ; t[now][q] + j <= m && q < vis[now].size() ; q ++){
						plc[vis[now][q]] = 1;
					}
		//			cout << plc << endl;
					break;
				}
			}
			res[i] = plc.count();
	//		cout << res[i] << endl;
		}
		for(int i = 1 ; i <= n ; i ++){
			printf("%d\n",res[i]);
		}
		return;
	}
}
namespace B{
	vector<int>vis[MAXN];
	int to[MAXN],a[MAXN];
	bool wtb[MAXN];
	bitset<MAXN>plc;
	int res[MAXN];
	int dfs(int x){
		wtb[x] = true;
		for(int i = 0 ; i < vis[x].size() ; i ++){
			plc[vis[x][i]] = 1;
		}
		if(wtb[to[x]]){
		//	cout << plc << endl;
			res[x] = plc.count();
			return res[x];
		}
		res[x] = dfs(to[x]);
		return res[x];
	}
	void solve(){
		for(int i = 1 ; i <= n ; i ++){
			a[i] = i;
			vis[i].push_back(i);
		}
		for(int i = 1 ; i <= k ; i ++){
			vis[a[sp[i][0]]].push_back(sp[i][1]);
			vis[a[sp[i][1]]].push_back(sp[i][0]);
			swap(a[sp[i][0]],a[sp[i][1]]);
		}
		for(int i = 1 ; i <= n ; i ++){
			to[i] = a[i];
		//	cout << to[i] << " ";
		}
	//	cout << endl;
		for(int i = 1 ; i <= n ; i ++){
			if(!wtb[i]){
				plc.reset();
				dfs(i);	
			}
		}
		for(int i = 1 ; i <= n ; i ++){
			printf("%d\n",res[i]);
		}
		return;
	}
}
#undef int 
int main(){
	#define int long long
	n = Read() , k = Read() , m = Read();
	for(int i = 1 ; i <= k ; i ++){
		sp[i][0] = Read();
		sp[i][1] = Read();
	}
	if(m > n * k){
		B::solve();
	}
	else if(m <= 1e7){
		A::solve();
	}
	else{
		
	}
	return 0;
}

碎碎念

USACO21JAN Result:

Bronze N/A
Silver N/A  
Gold 833pts
Platinum N/A

成功拔掉flag,进Platinum啦,skr~

最后promotion cutoff是750pts , 833pts是 Gold #69

成功靠着两道AC和一道50%的部分分晋级啦!

Feb可能会咕掉但是US Open应该会打一下

加油!

你可能感兴趣的:(比赛,题解)