记自己的第一次完整补题经验
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的很多题只是思维题,难度不太大,没有补的价值