首页 > 其他分享 >【题解】USACO 2023 January Sliver

【题解】USACO 2023 January Sliver

时间:2023-01-31 21:12:56浏览次数:42  
标签:cnt val int 题解 memset ans January sizeof Sliver

因为 Glod 打寄了,就来写写 Sliver 的题解吧,现在应该比赛结束了吧。

A.Find And Replace

题目分析:

我们可以对给定的串建出一种关系,也就是如果在上面的字符串中是字符 \(c_1\) 在下面的字符串对应的位置是字符 \(c_2\),那么我们就连边 \((c_1,c_2)\) 代表我们要将 \(c_1\) 连向变成 \(c_2\)。
可以发现如果某一个点的出度大于 \(1\) 那么一定无解,因为我们需要将这个字符变成多个字符,但是我们只能将这个字符变成最多一种。
这样建出来的图是只含有三种结构的:链、环、挂着链的环
对于链的处理很简单,就是沿着链的方向倒着处理一下就好了。
对于环显然就需要断环成链,也就是找到一个环上的点,把它和下一个点断开,也就是将这个点换成一个其他的字符 \(c_3\),然后从 \(c_3\) 再连向这个环上的点的下一个点,也就是将环变了的链,但是这样做需要有一个字符 \(c_3\) 它的出度为 \(0\)。
对于环加链其实和环是一样的,只不过我们这次找的 \(c_3\) 就可以直接是与环直接相连的链上的点,这样只需要一次操作就可以同时实现链上的点和我们断掉的点的需要,节省了一次操作。而且这样本质上是断环成链所以可以多出来一个出度为 \(0\) 的点。

这样其实代码不好写,因为找环很麻烦,但是环一定是一个强连通分量,所以直接 \(tarjan\) 缩点之后就变得很简单了。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+5;
struct edge{
	int nxt,to;
	edge(){}
	edge(int _nxt,int _to){
		nxt = _nxt,to = _to;
	}
}e2[N];
int top,tot,col,st[N],dfn[N],sz[N],low[N],color[N],chu[N],deg[N];
char s[200*N],t[200*N];
bool a[100][100],flag[N];
int get(char c){
	if(c >= 'a' && c <= 'z')	return c - 'a' + 1;
	return c - 'A' + 1 + 26;
}
void tarjan(int now){
	st[++top] = now;dfn[now] = low[now] = ++tot;
	for(int i=1; i<=52; i++){
		if(now == i)	continue;
		if(a[now][i]){
			int to = i;
			if(!dfn[to]){
				tarjan(to);
				low[now] = min(low[now],low[to]);
			}
			else if(!color[to])	low[now] = min(low[now],dfn[to]);
		}
	}
	if(dfn[now] == low[now]){
		++col;
		color[now] = col;sz[col]++;
		while(st[top] != now){
			color[st[top]] = col;sz[col]++;
			top--;
		}
		top--;
	}
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%d",&T);
	while(T--){
		bool tag1 = true,tag2 = false;
		scanf("%s",s+1);scanf("%s",t+1);
		int n = strlen(s+1);
		for(int i=1; i<=n; i++)	a[get(s[i])][get(t[i])] = true;
		for(int i=1; i<=52; i++){
			for(int j=1; j<=52; j++){
				if(a[i][j])	chu[i]++;
			}
		}
		for(int i=1; i<=52; i++){
			if(chu[i] > 1)	tag1 = false;
			else if(chu[i] == 0)	tag2 = true;
		}
		memset(chu,0,sizeof(chu));
		for(int i=1; i<=52; i++)	
			if(!dfn[i])	
				tarjan(i);
		for(int i=1; i<=52; i++)	if(a[i][i])	flag[color[i]] = true;
		for(int i=1; i<=52; i++){
			for(int j=1; j<=52; j++){
				if(i == j)	continue;
				if(a[i][j] && color[i] != color[j])	chu[color[i]]++,deg[color[j]]++;
			}
		}
		int ans = 0,tmp = 0;
		for(int i=1; i<=col; i++)	if(chu[i] == 1)	ans++;
		for(int i=1; i<=col; i++){
			if(deg[i] == 0 && sz[i] > 1){
				tmp += sz[i] + 1;
			}
			else if(deg[i] != 0 && sz[i] > 1){
				tag2 = true,ans += sz[i];
			}
		}
		if(!tag2 && tmp > 0)	tag1 = false;
		if(tag2)	ans += tmp;
		if(!tag1)	printf("-1\n");
		else	printf("%d\n",ans);
		memset(a,false,sizeof(a));memset(deg,0,sizeof(deg));memset(chu,0,sizeof(chu));
		memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(st,0,sizeof(st));
		memset(color,0,sizeof(color));memset(flag,false,sizeof(flag));memset(sz,0,sizeof(sz));
	}
	return 0;
}

B.Following Directions

题目分析:

可以发现对于一次修改我们影响的点最多 \(O(n)\) 个,所以只需要从我们修改的点开始 \(dfs\) 将原来的线路的贡献删除,将新线路的贡献加上即可。
对于贡献的初值就不能每个点直接 \(dfs\) 了,这样的复杂度为 \(O(n^3)\),所以就用一下拓扑排序就好了,因为我们的贡献显然是可以累积去传递的。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1505;
int n,cnt[N][N],val[N][N],deg[N][N];
char s[N][N];
void get(int x,int y){
	cnt[x][y]++;
	if(x > n || y > n)	return;
	if(s[x][y] == 'R')	get(x,y+1);
	else	get(x+1,y);
}
void del(int x,int y,int val){
	cnt[x][y] -= val;
	if(x > n || y > n)	return;
	if(s[x][y] == 'R')	del(x,y+1,val);
	else	del(x+1,y,val);
}
void add(int x,int y,int val){
	cnt[x][y] += val;
	if(x > n || y > n)	return;
	if(s[x][y] == 'R')	add(x,y+1,val);
	else	add(x+1,y,val);
}
int get_ans(){
	int res = 0;
	for(int i=1; i<=n; i++)	res += cnt[n+1][i] * val[n+1][i] + cnt[i][n+1] * val[i][n+1];
	return res;
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++){
		scanf("%s",s[i]+1);
		scanf("%d",&val[i][n+1]);
	}
	for(int i=1; i<=n; i++)	scanf("%d",&val[n+1][i]);
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			if(s[i][j] == 'R')	deg[i][j+1]++;
			else	deg[i+1][j]++;
		}
	}
	queue<pair<int,int> > q;
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			if(deg[i][j] == 0){
				q.push({i,j});
			}
		}
	}
	while(!q.empty()){
		int x = q.front().first,y = q.front().second;q.pop();
		if(x > n || y > n)	continue;
		cnt[x][y]++;
		if(s[x][y] == 'R'){
			deg[x][y+1]--;cnt[x][y+1] += cnt[x][y];
			if(deg[x][y+1] == 0)	q.push({x,y+1});
		}
		else{
			deg[x+1][y]--;cnt[x+1][y] += cnt[x][y];
			if(deg[x+1][y] == 0)	q.push({x+1,y});
		}
	}
	int ans = get_ans();
	printf("%d\n",ans);
	int Q;scanf("%d",&Q);
	while(Q--){
		int x,y;scanf("%d%d",&x,&y);
		int tmp = cnt[x][y];
		del(x,y,tmp);
		if(s[x][y] == 'R')	s[x][y] = 'D';
		else	s[x][y] = 'R';
		add(x,y,tmp);
		ans = get_ans();
		printf("%d\n",ans);
	}
	return 0;
}

C.Moo Route

题目分析:

因为我们如果要走过某一条边必然一来一回,所以可以预先减掉 \(2\),剩下的部分就是我们用反复横跳来消耗的。
为了使得转向次数最少,有一种显然地构造方式就是我们每一次都跳一个极长的区间,然后转向回到我们当前点,直到相对应的边不能再反复横跳就去下一个点。
这样做显然是正确的,因为如果没有跳极长的区间或提前去下一个点都是不会更优的。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int a[N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;scanf("%d",&n);
	for(int i=1; i<=n; i++)	scanf("%d",&a[i]),a[i] -= 2;
	for(int i=1; i<=n; i++)	printf("R");
	for(int i=n; i>=1; i--){
		if(a[i] == 0)	printf("L");
		else{
			while(a[i] != 0){
				int pos = i;
				for(;a[pos] > 0; pos--);
				pos++;
				for(int j=pos; j<=i; j++)	a[j] -= 2;
				for(int j=pos; j<=i; j++)	printf("L");
				for(int j=pos; j<=i; j++)	printf("R");
			}
			printf("L");
		}
	}
	return 0;
}

标签:cnt,val,int,题解,memset,ans,January,sizeof,Sliver
From: https://www.cnblogs.com/linyihdfj/p/17080726.html

相关文章

  • CF1098D 题解
    题意传送门对于一个元素个数大于\(1\)的可重集,每次取出两个数\(x,y\)合并。若\(x\ley\le2x\),则称其为危险合并。重复上述操作至无法合并。给你一个初始为空的可......
  • 【题解】[LNOI2022] 盒
    题目分析:我们可以对每一条边单独计算贡献,这样会发现贡献很好算:\[ans=\sum_{i=0}^{n-1}w_i\sum_{j=0}^S|j-s_i|\binom{i+j-1}{i-1}\binom{S-j+n-i-1}{n......
  • 【题解】P5169 xtq的异或和
    再强没有xtq!!!1思路多项式的正确用法。首先根据P4151[WC2011]最大XOR和路径的神秘结论,这里只需要任意求出原图的一棵生成树,以及所有只包含一条非树边的简单环就可以维......
  • 调用后台接口实现Excel导出功能以及导出乱码问题解决
    实现效果在导出表格数据的时候,通常分为两种情况页面列表数据导出接口返回数据导出这里主要介绍接口返回数据导出,关于页面的列表数据导出,请看另一篇:vue3+element表格......
  • CF1400E Clear the Multiset 题解 贪心+分治
    题目链接:http://codeforces.com/problemset/problem/1400/E题目大意:给定一个长度为\(n\)数列\(\{a_n\}\),你可以进行如下操作:操作1:任意选择一个区间\([l,r]\),使区间内......
  • P6327 区间加区间sin和 题解
    P6327区间加区间sin和题解第一道Ynoi(捂脸题目描述给出一个长度为\(n\)的整数序列\(a_1,a_2,\ldots,a_n\),进行\(m\)次操作,操作分为两类。操作\(1\):给出\(l,r,v......
  • [SDOI2008] 仪仗队【题解】
    题目描述作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的\(N\timesN\)的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所......
  • Luogu P4145 上帝造题的七分钟 2 / 花神游历各国 题解
    Luogu链接:上帝造题的七分钟2/花神游历各国${\scr\color{Orchid}{\text{Solution}}}$题目大意支持两种操作:区间开方(向下取整)区间求和分析发现线段树容易实......
  • AT dp 26 题 题解
    题单:https://www.luogu.com.cn/training/100578#problems嘛虽然是26题,但是简单的题就不想写了...就写绿题及以上的吧目录EIJMOPQRSTUVE对重量dp,设\(dp[i][v]\)表......
  • uniapp webview中动态设置公众号文章标题不显示问题解决
    设置在onLoad中可能会引起偶发性无效。解决方案:1、改写在onReady生命周期中。2、用setTimeout设置延迟。 onReady(){this.timers=setTimeout(()=>{......