CF 2022寒假练习
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
链接
题目大意
现有一排 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
链接
题目大意
公园是一棵树,其中有 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
链接
题目大意
给一个数组 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
链接
题目大意
现有一大小为 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
链接
题目大意
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} 2n−1个元素的数列。当这个操作做了 n n n次后,数列中就只剩下一个元素 v v v了。当第 n − 2 k ( k ∈ N ∗ ) n-2k(k\in N^*) n−2k(k∈N∗)次操作时,合并操作为OR;第 n − ( 2 k + 1 ) ( k ∈ N ∗ ) n-(2k+1)(k\in N^*) n−(2k+1)(k∈N∗)次操作时,合并操作为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
链接
题目大意
用红、绿、蓝三种颜色的气球装饰桌子,要求每个桌子上三个气球的颜色不能完全相同。现给出三种颜色气球的个数 r , g , b r,g,b r,g,b,求这些气球最多能装饰多少桌子。
解析
先将三种气球的数量从小打到排序。若最少和次少的气球的数量之和大于最多的气球的数量的两倍,则每桌的三个气球不同色为最优解。否则,每桌放两个数量最多的气球和一个数量较少的气球为最优解。证明如下:
设 a , b , c a,b,c a,b,c分别为三种颜色气球的数目,且 a ≤ b ≤ c a≤b≤c a≤b≤c。则 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+b⇔a+b+c>3a+3b⇔c>2a+2b⇔2c>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?
链接
题目大意
现有 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;
}