CF Round #582 (Div. 3)

记自己的第一次完整补题经验
CF初体验(误)
没精力参加rate,就赛后补题了

A - Chips Moving

贼水,把数据处理成奇偶数处理就行了(略)

B - Bad Prices

有点思维难度,太羞耻了,题目没读懂,看的别人的代码,一看就明白了,从后往前做,始终维持着一个最小的值
一开始还想着用线段树维持最小值,没想到这个区间不是普通的区间,而是后缀,有更方便的方法

#include<iostream>
#include<algorithm>
using namespace std;
int m[400000];
int main()
{
	int T,n,m1,m0;
	cin>>T;
	while(T--)
	{
		scanf("%d",&n);
		m0=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&m[i]);
		}
		m1=m[n];
		for(int i=n-1;i>=1;i--)
		{
			if(m[i]>m1) m0++;
			else m1=m[i];
		}
		cout<<m0<<endl;
	}
}

C - Book Reading

第一次打表,比较丑陋
还有就是数据范围得用longlong,记住所有的数据都得用,别偷懒就几个用,我第一次就这样错了

#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
int m[10][10]={{0},
{1,2,3,4,5,6,7,8,9,0},
{2,4,6,8,0},
{3,6,9,2,5,8,1,4,7,0},
{4,8,2,6,0},
{5,0},
{6,2,8,4,0},
{7,4,1,8,5,2,9,6,3,0},
{8,6,4,2,0},
{9,8,7,6,5,4,3,2,1,0}};
int t[10]={1,10,5,10,5,2,5,10,5,10};
int main()
{
	int T;
	ll ans,m2,m3,n,m1,m0;
	cin>>T;
	while(T--)
	{
		scanf("%lld%lld",&n,&m0);
		ans=0;
		m1=m0%10;
		n=n/m0;
		m2=n/t[m1];
		//cout<<m2<<endl;
		m3=n%t[m1];
		for(int i=0;i<t[m1];i++)
		{
			ans+=m[m1][i];
		}
		//cout<<ans<<endl;
		ans*=m2;
		//cout<<ans<<endl;
		for(int i=0;i<m3;i++)
		{
			ans+=m[m1][i];
		}
		cout<<ans<<endl;
	}
}

D1 - Equalizing by Division (easy version)

有点惭愧,不算太难,还是看了别人的代码
这种不涉及任何算法的模拟题,对我来说应该是完全能够自己解决的

#include<bits/stdc++.h>
using namespace std;
const int MAXM=200005;
struct A
{
	int n;
	vector<int> l;
}que[MAXM];
int main()
{
	int n,m,m1,M=0,num;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&m1);
		M=max(M,m1);
		num=0;
		while(m1)
		{
			que[m1].l.push_back(num++);
			que[m1].n++;
			m1/=2;
		}
	}
	int ans=-1,d;
	for(int i=1;i<=M;i++)
	{
		if(que[i].l.size()<m) continue;
		sort(que[i].l.begin(),que[i].l.end());
		d=0;
		for(int j=0;j<m;j++)
		{
			d+=que[i].l[j];
		}
		if(ans==-1) ans=d;
		else ans=min(ans,d);
	}
	cout<<ans<<endl;
}

D2 - Equalizing by Division (hard version)

不知道为什么我直接用的上一题的代码,就过了

今天先这样吧,下面的题目开始有难度了,明天再肝吧,等我明天学会线性基再回来


E - Two Small Strings

逻辑思考题,没有任何算法思想,主要,额,就是分情况讨论,一定要,分的好分的准,前往别漏想哪种情况,开始做前感觉很简单,刷刷就写完了,结果啪啪打脸,思维还是不够严密严谨

#include<iostream>
using namespace std;
char ans[3]={'a','b','c'};
char t1[3],t2[3];
int w1[3],w2[3];
int n;
void q(int m1,int m2,int m3)
{
	int q1[3];
	q1[0]=m1;q1[1]=m2;q1[2]=m3;
	for(int i=0;i<=2;i++){
		for(int j=1;j<=n;j++){
			cout<<ans[q1[i]];
		}
	}
}
void resolve(int x1,int x2,int y1,int y2)
{
if(x1!=x2 && y1!=y2){
	if(x1==y1){
		q((x1+1)%3,(x1+2)%3,x1);
		return;
	}
	if(x2==y2){
		q(x2,(x2+1)%3,(x2+2)%3);
		return;
	}
	if(x1==y2 && x2==y1){
		q(x1,3-x1-x2,x2);
		return;
	}
}
	if(x1!=x2){
		for(int i=1;i<=n;i++){
			cout<<ans[x1]<<ans[3-x1-x2]<<ans[x2];
		}
		return;
	}
	if(y1!=y2){
		for(int i=1;i<=n;i++){
			cout<<ans[y1]<<ans[3-y1-y2]<<ans[y2];
		}
		return;
	}
	for(int i=1;i<=n;i++){
		cout<<ans[y1]<<ans[(y1+1)%3]<<ans[(y1+2)%3];
	}
}
int main()
{
	int m1,m2,m3,t;
	cin>>n;
	cin>>t1>>t2;
	for(int i=0;i<=1;i++){
		w1[i]=t1[i]-'a';
		w2[i]=t2[i]-'a';
	}
	cout<<"YES"<<endl;
	resolve(w1[0],w1[1],w2[0],w2[1]);
}

G - Path Queries

一开始理解错了,以为是要求树上两点距离小于某一值的点对数,结果远比这个简单,实际上问的是,求两点之间每条路经长小于定值
先sort对路径长进行排序,然后离线sort所有所有询问,运用并查集做联通块,若是按大小顺序遍历路径长,如果小于当前询问,就unit路径起点和终点的并查集;如果大于就进行下一个询问

#include<iostream>
#include<algorithm>
using namespace std;
const int MAXM = 200005;
#define ll long long
int pre[MAXM],size[MAXM];
ll sum=0,ans[MAXM];
int find_pre(int x)
{
	if(pre[x]==x)
	{
		return x;
	}
	return pre[x]=find_pre(pre[x]);
}
bool issame(int x,int y)
{
	return find_pre(x)==find_pre(y);
}
void init(int n)
{
	int i;
	for(i=0;i<=n;i++)
	{
		pre[i]=i;
		size[i]=1;
	}
}
void unit(int x,int y)
{
	int rootx,rooty;
	rootx=find_pre(x);
	rooty=find_pre(y);
	if(rootx==rooty)
	{
		return;
	}
	pre[rootx]=rooty;
	sum+=(ll)size[rootx]*size[rooty];
	size[rooty]+=size[rootx];
}
struct a1
{
	int x,y;
	ll v;
}a[MAXM];
struct b1
{
	int id;
	ll v;
}b[MAXM];
bool cmp1(a1 x1,a1 y1)
{
	return x1.v<y1.v;
}
bool cmp2(b1 x1,b1 y1)
{
	return x1.v<y1.v;
}
int main()
{
	int T,N,M;
	cin>>N>>M;
	for(int i=1;i<=N-1;i++){
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
	}
	init(N);
	for(int i=1;i<=M;i++){
		scanf("%d",&b[i].v);
		b[i].id=i;
	}
	sort(a+1,a+N,cmp1);
	sort(b+1,b+M+1,cmp2);
	int j=1;
	for(int i=1;i<=N-1;i++){
		if(a[i].v<=b[j].v){
			unit(a[i].x,a[i].y);
		}
		else{
			ans[b[j].id]=sum;
			j++;
			if(j==M+1) break;
			i--;
		}
	}
	for(;j<=M;j++){
		ans[b[j].id]=sum;
	}
	for(int i=1;i<=M;i++){
		cout<<ans[i]<<' ';
	}
}

F - Unstable String Sort

这题和其他题就完全不一样了,需要算法知识
看到一条路径上的前面的一定要比后面的字母小,我想到了拓扑排序,但还有一个问题,就是拓扑排序碰到环就会停下,无法继续前进,所以需要先用Tarjan将强联通分量缩点,这样图一定不是强连通图,然后用拓扑排序分配字母就行了,对我来说,难度还是很大的

#include<iostream>
#include<stack>
#include<vector>
#include<queue>
using namespace std;

const int MAXM=200005;
int n,k,cnt,cntb,cnt1,cnt2;
struct
{
	int v,w,nxt;
}edge[MAXM*3],edge2[MAXM*3];
bool instack[MAXM];
int dfn[MAXM],low[MAXM],f[MAXM],head[MAXM],head2[MAXM],in[MAXM];
char du[MAXM];
stack<int> s;

void add_edge(int u,int v)
{
	edge[++cnt1].v = v;
	edge[cnt1].nxt = head[u];
	head[u] = cnt1;
}
void add_edge2(int u,int v)
{
	edge2[++cnt2].v = v;
	edge2[cnt2].nxt = head2[u];
	head2[u] = cnt2;
}
void Tarjan(int u)
{
	++cnt;
	dfn[u]=low[u]=cnt;
	s.push(u);
	instack[u]=true; 
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v = edge[i].v;
		if(!dfn[v])
		{
			Tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(instack[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		++cntb;
		int node;
		do
		{
			node=s.top();
			s.pop();
			instack[node]=false;
			f[node]=cntb;
		}while(node!=u);
	}
}
bool topo()
{
    //priority_queue< int ,vector< int >,greater< int > >q;
    queue<int> q;
    int k1;
    for(int i=1;i<=n;i++){
    	for(int j=head[i];j;j=edge[j].nxt){
    		k1 = edge[j].v;
    		if(f[i]!=f[k1]){
    			add_edge2(f[i],f[k1]);
    			in[f[k1]]++;
    		}
		}
	}
    for(int i=1;i<=cntb;i++)  //n  节点的总数
        if(in[i]==0) q.push(i);  //将入度为0的点入队列
    int id=0;
    while(!q.empty())
    {
        int p=q.front(); q.pop(); // 选一个入度为0的点,出队列
        if(id<26) id++;
        du[p]='a'+id-1;
        for(int i=head2[p];i;i=edge2[i].nxt)
        {
            int y=edge2[i].v;
            in[y]--;
            if(in[y]==0)
                q.push(y);  
        }
    }
    if(id<k) return 0;
    else return 1;
}
void duru(int n)
{
	int m1,m2;
	for(int j=1;j<=2;j++){
		scanf("%d",&m1);
		for(int i=1;i<=n-1;i++){
			scanf("%d",&m2);
			add_edge(m1,m2);
			m1=m2;
		}
	}
}
int main()
{
	cin>>n>>k;
	duru(n);
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			Tarjan(i);
		}
	}
	bool quba;
	quba=topo();
	/*for(int i=1;i<=n;i++){
		cout<<f[i]<<endl;;
	}*/
	if(quba == true){
		cout<<"YES"<<endl;
		for(int i=1;i<=n;i++){
			cout<<du[f[i]];
		}
}
	else{
		cout<<"NO"<<endl;
	}	
	cout<<endl;
	return 0;
} 

结语

第一次补完了一次比赛中所有的题,前后十几天的时间(中间我去干其他事了,其实真正补题的时间就几天),之前我做题从来都不补题,最多看个思想,感觉要是每次都能补完一套题,提升远比我之前的蜻蜓点水的方法要大得多,以后不做Div3了,除非要上分的时候,补题还是补Div2吧,Div3的很多题只是思维题,难度不太大,没有补的价值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值