CF 2022寒假练习

CF 2022寒假练习

CF_2A Winner

链接

CF_2A Winner

题目大意

有一个游戏,由 n n n个玩家参与,每一轮会有一个玩家获得 s s s点数。游戏最后一轮结束后,点数最多的玩家获胜。如果游戏结束后,由多个玩家获得最多点数 m m m,则他们当中最先获得至少 m m m点的玩家获胜。

解析

进行两遍模拟,第一遍求出所有点数最多的玩家,第二遍求出最终获胜者。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
struct str
{
	int val,tim;
}edmax[1001],x;
int n,p[1001],tot0[1001],tot1[1001],cnt,maxx,sta[2000001],ans;
string s[1001],lst[1001];
bool flag[1001];
map<string,int> m;
bool cmp(str a,str b)
{
	if(a.val>b.val)
		return true;
	else if(a.val==b.val&&a.tim<b.tim)
		return true;
	else
		return false;
}
int main()
{
	for(int i=1;i<=1000;i++)
		tot0[i]=tot1[i]=1000000;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		scanf("%d",&p[i]);
		if(!m[s[i]])
		{
			cnt++;
			m[s[i]]=cnt;
			lst[cnt]=s[i];
		}
		tot0[m[s[i]]]+=p[i];
	}
	for(int i=1;i<=cnt;i++)
		maxx=max(maxx,tot0[i]);
	for(int i=1;i<=n;i++)
	{
		tot1[m[s[i]]]+=p[i];
		if(tot1[m[s[i]]]>=maxx&&tot0[m[s[i]]]==maxx)
		{
			ans=m[s[i]];
			break;
		}
	}
	cout<<lst[ans];
	return 0;
}

CF_545C Woodcutters

链接

CF_545C Woodcutters

题目大意

现有一排 n n n颗树,第 i i i棵树的高度为 a i a_i ai。主人公要砍树,被砍的树可能往左倒或往右倒,求在不砸到其他树的情况下,主人公最多能砍多少棵树。

解析

动态规划,对每棵树,讨论不砍、砍下往左倒、砍下往右倒三种情况,看如何使当前砍树的数量最大化。注意,如果某棵树不砍,那么它要继承上一棵树的状态。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
struct str
{
	int tot,maxx;
}f[100001][2];
int n,x[100001],h[100001],ans;
bool flag0,flag1;
bool check0(int p)
{
	for(int i=p;1<=i&&x[p]-h[p]<=x[i];i--)
		if(i!=p)
			return false;
	return true;
}
bool check1(int p)
{
	for(int i=p;i<=n&&x[i]<=x[p]+h[p];i++)
		if(i!=p)
			return false;
	return true;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&x[i],&h[i]);
	f[0][1].maxx=-2000000000;
	for(int i=1;i<=n;i++)
	{
		flag0=false;
		if(check0(i))
		{
			flag0=true;
			f[i][0].tot=f[i-1][0].tot+1;
			if(f[i-1][1].maxx<x[i]-h[i])
				f[i][0].tot=max(f[i][0].tot,f[i-1][1].tot+1);
		}
		if(!flag0)
		{
			f[i][0].tot=f[i-1][0].tot;
			f[i][0].maxx=f[i-1][0].maxx;
		}
		flag1=false;
		if(check1(i))
		{
			flag1=true;
			f[i][1].tot=max(f[i-1][0].tot+1,f[i-1][1].tot+1);
			f[i][1].maxx=x[i]+h[i];
		}
		if(!flag1)
		{
			f[i][1].tot=f[i-1][1].tot;
			f[i][1].maxx=f[i-1][1].maxx;
		}
	}
	ans=max(f[n][0].tot,f[n][1].tot);
	printf("%d",ans);
	return 0;
}

CF_580C Kefa and Park

链接

CF_580C Kefa and Park

题目大意

公园是一棵树,其中有 n n n个餐馆,其中某些餐馆有猫。主人公害怕猫,他希望自己去餐馆的路上连续遇见的猫不超过 m m m只,求主人公最多能去的餐馆数。

解析

一遍树上DFS即可,剪枝时注意阻止主人公的猫是“连续遇见”的。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
struct str
{
	int to,nxt;
}edg[100001];
int n,m,a[100001],lst[100001],cnt,x,y,ans;
bool vis[100001];
vector<int> vct[100001];
void add(int frm,int to)
{
	cnt++;
	edg[cnt].to=to;
	edg[cnt].nxt=lst[frm];
	lst[frm]=cnt;
}
bool check(int n)
{
	if(vct[n].size()==1&&n!=1)
		return true;
	else
		return false;
}
void dfs(int p,int lp,int tot)
{
	if(!a[p])
		tot=0;
	else
	{
		if(a[lp])
			tot++;
		else
			tot=1;
	}
	if(tot>m)
		return;
	if(check(p))
	{
		ans++;
		return;
	}
	else
	{
		for(int i=0;i<vct[p].size();i++)
		{
			if(!vis[vct[p][i]])
			{
				vis[vct[p][i]]=true;
				dfs(vct[p][i],p,tot);
				vis[vct[p][i]]=false;
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d",&x,&y);
		vct[x].push_back(y);
		vct[y].push_back(x);
		//add(x,y);
		//add(y,x);
	}
	vis[1]=true;
	dfs(1,0,0);
	printf("%d",ans);
	return 0;
}

CF_1352E Special Elements

链接

CF_1352E Special Elements

题目大意

给一个数组 a a a,定义元素 a i a_i ai为“特殊元素”当且仅当存在数对 l , r ( l < r ) l,r(l<r) l,r(l<r)使得 a i = ∑ j = l r a j a_i=\sum^{r}_{j=l} a_j ai=j=lraj,求数组 a a a中“特殊元素”的个数。

解析

前缀和枚举即可,注意数组中的元素可能相同。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
int t,n,a[10001],s[10001],ans;
int b[10001],c[10001];
int main()
{
	scanf("%d",&t);
	while(t)
	{
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(c,0,sizeof(c));
		memset(s,0,sizeof(s));
		ans=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			b[a[i]]++;
			s[i]=s[i-1]+a[i];
		}
		for(int i=1;i<=n;i++)
			for(int j=i+1;j<=n;j++)
				if(s[j]-s[i-1]<=8000&&b[s[j]-s[i-1]])
					c[s[j]-s[i-1]]=b[s[j]-s[i-1]];
		for(int i=1;i<=8000;i++)
			if(c[i])
				ans+=c[i];
		printf("%d\n",ans);
		t--;
	}
	return 0;
}

CF_1368B Codeforces Subsequences

链接

CF_1368B Codeforces Subsequences

题目大意

给一数字 k k k,求一个最短字符串 s s s,使得该字符串当中的字符可以在不改变字符顺序的前提下有至少 k k k种方式组合出字符串 codeforces \texttt{codeforces} codeforces

解析

如果最终呈现的字符串是 ccooooddeeeeefffforrccccessss \texttt{ccooooddeeeeefffforrccccessss} ccooooddeeeeefffforrccccessss形式的,那么字符串中每“种”字符的个数就决定了最终的方案数。例如,字符串 ccooooddeeeeefffforrccccessss \texttt{ccooooddeeeeefffforrccccessss} ccooooddeeeeefffforrccccessss中,字符 c,o,d,e,f,o,r,c,e,s \texttt{c,o,d,e,f,o,r,c,e,s} c,o,d,e,f,o,r,c,e,s分别有 2 , 4 , 2 , 5 , 4 , 1 , 2 , 4 , 1 , 4 2,4,2,5,4,1,2,4,1,4 2,4,2,5,4,1,2,4,1,4个,那么该字符串的方案数就为 2 × 4 × 2 × 5 × 4 × 1 × 2 × 4 × 1 × 4 = 10240 2\times 4\times 2\times 5\times 4\times 1\times 2\times 4\times 1\times 4=10240 2×4×2×5×4×1×2×4×1×4=10240个。因此,相同方案数下,每“种”字符分配到的个数越均匀,字符串的长度就越短。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const ll maxn=1e16;
char ch[10]={'c','o','d','e','f','o','r','c','e','s'};
ll k,tot[10],cnt;
bool che()
{
	ll pro=1;
	for(int i=0;i<10;i++)
		pro=pro*tot[i];
	if(pro>=k)
		return true;
	else
		return false;
}
int main()
{
	for(int i=0;i<10;i++)
		tot[i]=1;
	scanf("%lld",&k);
	while(!che())
	{
		tot[cnt]++;
		cnt=(cnt+1)%10;
	}
	for(int i=0;i<10;i++)
		for(int j=0;j<tot[i];j++)
			putchar(ch[i]);
	return 0;
}

CF_377A Maze

链接

CF_377A Maze

题目大意

现有一大小为 n × m n\times m n×m的迷宫,其中 # \texttt{\#} # . \texttt{.} .分别代表迷宫中的“”墙和“”路。当前的迷宫是连通的,你需要求出在增加了 k k k个“墙”后仍然连通的迷宫,新增的“墙”用 X \texttt{X} X表示。

解析

先对迷宫用BFS进行遍历,再按照每个点的深度从最深的点开始放“墙”,就能保证最后得到的迷宫是连通的。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;
struct str
{
	int t,ii,jj;
}s0;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int n,m,k,tot[250001],totm[501][501],maxn,totn[250001];
bool vis[501][501],wall[501][501],walln[501][501];
char ch[501][501];
queue<str> q;
bool check(str s)
{
	if(1<=s.ii&&s.ii<=n&&1<=s.jj&&s.jj<=m&&!vis[s.ii][s.jj]&&!wall[s.ii][s.jj])
		return true;
	else
		return false;
}
void bfs()
{
	int i0,j0;
	str sn;
	i0=j0=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			if(!wall[i][j])
			{
				i0=i,j0=j;
				break;
			}
		if(i0&&j0)
			break;
	}
	s0.t=0,s0.ii=i0,s0.jj=j0;
	vis[s0.ii][s0.jj]=true;
	tot[s0.t]++;
	totm[s0.ii][s0.jj]=s0.t;
	q.push(s0);
	while(!q.empty())
	{
		s0=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{
			sn.t=s0.t+1;
			sn.ii=s0.ii+dir[i][0];
			sn.jj=s0.jj+dir[i][1];
			if(check(sn))
			{
				vis[sn.ii][sn.jj]=true;
				tot[sn.t]++;
				maxn=max(maxn,sn.t);
				totm[sn.ii][sn.jj]=sn.t;
				q.push(sn);
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch[i]+1);
		for(int j=1;j<=m;j++)
			if(ch[i][j]=='#')
				wall[i][j]=true;
	}
	bfs();
	for(int i=maxn;i>=1;i--)
	{
		if(k>tot[i])
		{
			totn[i]=tot[i];
			k-=totn[i];
		}
		else
		{
			totn[i]=k;
			k=0;
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(totn[totm[i][j]])
			{
				totn[totm[i][j]]--;
				walln[i][j]=true;
			}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(wall[i][j])
				putchar('#');
			else if(walln[i][j])
				putchar('X');
			else
				putchar('.');
		}
		putchar('\n');
	}
	return 0;
}

CF_1363C Game On Leaves

链接

CF_1363C Game On Leaves

题目大意

Ayush与Ashish在玩游戏。游戏规则为,有一棵树,玩家可以删除树上任意一个叶节点。有一个特殊节点 x x x,先删除节点 x x x的玩家获胜。Ayush为先手,在双方都采用最佳策略的前提下,求获胜者。

解析

如果节点 x x x为叶节点,则先手必胜;如果节点数为奇数,则先手必胜,否则,后手必胜。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;
int t,n,x,u,v,tot[1001];
int main()
{
	scanf("%d",&t);
	while(t)
	{
		memset(tot,0,sizeof(tot));
		scanf("%d%d",&n,&x);
		for(int i=1;i<=n-1;i++)
		{
			scanf("%d%d",&u,&v);
			tot[u]++,tot[v]++;
		}
		if(tot[x]<=1)
			printf("Ayush\n");
		else if(n&1)
			printf("Ashish\n");
		else
			printf("Ayush\n");
		t--;
	}
	return 0;
}

CF_339D Xenia and Bit Operations

链接

CF_339D Xenia and Bit Operations

题目大意

现有一数列 a a a,其中有 2 n 2^n 2n个元素。将数列 a a a中相邻两个元素合并,可以得到有 2 n − 1 2^{n-1} 2n1个元素的数列。当这个操作做了 n n n次后,数列中就只剩下一个元素 v v v了。当第 n − 2 k ( k ∈ N ∗ ) n-2k(k\in N^*) n2k(kN)次操作时,合并操作为OR;第 n − ( 2 k + 1 ) ( k ∈ N ∗ ) n-(2k+1)(k\in N^*) n(2k+1)(kN)次操作时,合并操作为XOR。

题目会对数列中的元素进行动态修改,并要求求出每次修改后的 v v v值。

解析

用线段树或树状数组动态维护数列即可。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;
const int maxn=131072;
struct str
{
	int l,r,v,f;
}t[4*maxn+1];
int n,m,x,p,b;
void build(int k,int l,int r)
{
	t[k].l=l;
	t[k].r=r;
	if(l==r)
	{
		t[k].f=0;
		return;
	}
	int m=(l+r)>>1;
	build(k*2,l,m);
	build(k*2+1,m+1,r);
	t[k].f=t[k*2].f+1;
}
void add(int k,int p,int v)
{
	if(t[k].l==t[k].r)
	{
		t[k].v=v;
		return;
	}
	if(p<=t[k*2].r)
		add(k*2,p,v);
	else
		add(k*2+1,p,v);
	if(t[k].f&1)
		t[k].v=t[k*2].v|t[k*2+1].v;
	else
		t[k].v=t[k*2].v^t[k*2+1].v;
}
int main()
{
	scanf("%d%d",&n,&m);
	build(1,1,(1<<n));
	for(int i=1;i<=(1<<n);i++)
	{
		scanf("%d",&x);
		add(1,i,x);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&p,&b);
		add(1,p,b);
		printf("%d\n",t[1].v); 
	}
	return 0;
}

CF_478C Table Decorations

链接

CF_478C Table Decorations

题目大意

用红、绿、蓝三种颜色的气球装饰桌子,要求每个桌子上三个气球的颜色不能完全相同。现给出三种颜色气球的个数 r , g , b r,g,b r,g,b,求这些气球最多能装饰多少桌子。

解析

先将三种气球的数量从小打到排序。若最少和次少的气球的数量之和大于最多的气球的数量的两倍,则每桌的三个气球不同色为最优解。否则,每桌放两个数量最多的气球和一个数量较少的气球为最优解。证明如下:

a , b , c a,b,c a,b,c分别为三种颜色气球的数目,且 a ≤ b ≤ c a≤b≤c abc。则 a + b + c 3 \frac{a+b+c}{3} 3a+b+c为第一种情况可装饰的桌子数, a + b {a+b} a+b为第二种情况可装饰的桌子数。

a + b + c 3 > a + b ⇔ a + b + c > 3 a + 3 b ⇔ c > 2 a + 2 b ⇔ c 2 > a + b \frac{a+b+c}{3}>{a+b}\\ \Leftrightarrow a+b+c>3a+3b\\ \Leftrightarrow c>2a+2b\\ \Leftrightarrow \frac{c}{2}>a+b 3a+b+c>a+ba+b+c>3a+3bc>2a+2b2c>a+b

因此,当 c 2 > a + b \frac{c}{2}>a+b 2c>a+b时,答案为 c 2 \frac{c}{2} 2c;否则,答案为 c 2 \frac{c}{2} 2c

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
ll c[3],ans,x;
int main()
{
	scanf("%lld%lld%lld",&c[0],&c[1],&c[2]);
	sort(c,c+3);
	if(c[0]+c[1]>=c[2]/2)
		printf("%lld",(c[0]+c[1]+c[2])/3);
	else
		printf("%lld",c[0]+c[1]);
	return 0;
}

CF_20C Dijkstra?

链接

CF_20C Dijkstra?

题目大意

现有 n n n个点, m m m条无向边,求点 1 1 1与点 n n n之间的最短路。若无最短路,则输出 − 1 -1 1

解析

由于题目对“Dijkstra算法”存在“?”,因此易得这道题应当用“Dijkstra算法(堆优化)”或“SPFA算法”等其他最短路算法解题。因为本人SPFA写得熟,所以就用SPFA来解。

代码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const ll maxa=1000000,maxb=100000000007;
struct str
{
	int to,len,nxt;
}edg[maxa+1];
ll n,m,u,v,w,cnt,s,t,tot;
ll ls[maxa+1],dis[2][maxa+1],pth[maxa+1];
queue<ll> que;
void add(ll u,ll v,ll w)
{
	cnt++;
	edg[cnt].to=v;
	edg[cnt].len=w;
	edg[cnt].nxt=ls[u];
	ls[u]=cnt;
}
int main()
{
	for(int i=1;i<=maxa;i++)
		dis[1][i]=maxb;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	dis[1][1]=0;
	que.push(1);
	while(!que.empty())
	{
		s=que.front();
		que.pop();
		for(int i=ls[s];i;i=edg[i].nxt)
		{
			t=edg[i].to;
			if(dis[1][s]+edg[i].len<dis[1][t])
			{
				dis[1][t]=dis[1][s]+edg[i].len;
				dis[0][t]=s;
				que.push(t);
			}
		}
	}
	if(dis[1][n]==maxb)
		printf("-1");
	else
	{
		t=n;
		tot++;
		pth[tot]=t;
		while(dis[0][t])
		{
			tot++;
			pth[tot]=dis[0][t];
			t=dis[0][t];
		}
		for(int i=tot;i>=1;i--)
			printf("%lld ",pth[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值