做题记录2021.8.29 牛客IOI周赛28

昨天晚上参加牛客IOI周赛普及组,以下是个人做题记录:

1.

在这里插入图片描述在这里插入图片描述
这题很简单,不难发现操作会形成循环。所以有效次数为x mod n,直接模拟即可。

#include <iostream>
#include <string>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    string s;
    long long len,x;
    cin>>len>>x;
    cin>>s;
    x%=len;
    for(int i=0; i<x; i++) {
    	//s=s[0]+s.substr(1)  不能这样写
        s.append(1,s[0]);
        s=s.substr(1);
    }
    cout<<s;
    return 0;
}

2.

在这里插入图片描述
这题没有正确思路,我是这样想的:枚举LIS的可能的起始位置,并选其第一个元素,第i个元素是对应序列中第一个比第i-1个元素大的值。如果找不到,则认为是对应序列的最后一个值。
这其实是贪心的思路,其实我原本想骗一小部分分,没想到对了70%……正解思路DP暂时不太懂,以后有时间慢慢磨把。

//错误代码
#include <cstdio>
#include <algorithm>
#include <vector>
const int MAXN=1001,MAXK=5001;
using namespace std;
int a[MAXN],n;
vector<int> b[MAXN];
int lis() {
	vector<int> dp(n+2);
	vector<int>::iterator it=dp.begin();
	dp[1]=a[1];
	int len=1;
	for(int i=2; i<=n; i++) {
		if(a[i]>dp[len]) {
			dp[++len]=a[i];
		}
		else {
			int loc=lower_bound(it+1,it+len+1,a[i])-it;
			dp[loc]=a[i];
		}
	}
	return len;
}

int main() {
    int k,res=0;
    scanf("%d%d",&k,&n);
    for(int i=1; i<=n; i++) {
    	for(int j=0; j<k; j++) {
    		int x;
    		scanf("%d",&x);
    		b[i].push_back(x);
		}
	}
	for(int i=1; i<=n; i++) {  //枚举LIS的起始位置
		a[i]=b[i][0];
		for(int j=i+1; j<=n; j++) {
			int loc=upper_bound(b[j].begin(),b[j].end(),a[j-1])-b[j].begin();
			if(loc==(int)b[j].size()) 	a[j]=b[j][0];
			else	a[j]=b[j][loc];
		}
		res=max(res,lis());
	}
	printf("%d",res);
    return 0;
}

3.

暴力BFS竟然能通过,说明此题数据够水啊……正解是倒着建边正序遍历,这样每个点只经过一次。与我写的这篇博客里的第二题类似。可以用std::set完成题目中要求的去重与排序工作。

#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
const int M=100001;
using namespace std;
struct graph {
    int next,to;
    graph():next(-1),to(-1) {}
    graph(int n,int t):next(n),to(t) {}
};
vector<graph> G;
int fir[M],n;
inline void add(int u,int v,int &i) {
    G.push_back(graph(fir[u],v));
    fir[u]=i++;
}
bool vis[M];
inline void bfs(int st) {  //返回能到达的最小的点
    int res=st;
    queue<int> q;
    q.push(st);
    while(!q.empty()) {
        st=q.front();
        q.pop();
        vis[st]=1;
        res=min(res,st);
        for(int i=fir[st]; ~i; i=G[i].next) {
            int to=G[i].to;
            if(!vis[to]) {
                q.push(to);
            }
        }
    }
}

int main(){
    int m,k=0;
    set<int> res;
    scanf("%d%d",&n,&m);
    fill(fir+1,fir+n+1,-1);
    for(int i=0; i<m; i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        add(v,u,k);
    }
    for(int i=1; i<=n; i++) {
    	if(!vis[i]) {
    		bfs(i);
    		res.insert(i);
		}
	}
    set<int>::iterator it=res.begin();
    printf("%d",*it);
    while(++it!=res.end()) {
        printf(" %d",*it);
    }
    return 0;
}

4.

在这里插入图片描述
这其实是上一次周赛的第四题,不过由于本次的不会做而且题解也看不懂,所以换成这个。
这题正解是floyed。op=1时说明x点“通”了,任何点都可以通过x点中转,此时以x点为中转点进行floyed即可。注意为了降低复杂度,已经“通”的点就不要再遍历了。注意要对起点先跑一遍
op=2时,根据题意输出即可。注意更新起点。

#include <cstdio>
#include <algorithm>
#define INF 0x3f3f3f3f
const int M=301;
using namespace std;
int G[M][M];
bool xd[M];
void floyed(int n,int mid) {
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            G[i][j]=min(G[i][j],G[i][mid]+G[mid][j]);
        }
    }
}

int main() {
    int n,m,s,q;
    scanf("%d%d%d%d",&n,&m,&s,&q);
    for(int i=1; i<=n; i++) {
        fill(G[i], G[i]+n+1,INF);
        G[i][i]=0;
    }
    for(int i=0; i<m; i++) {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        if(u!=v)    G[u][v]=min(G[u][v], w);
    }
    floyed(n, s);
    xd[s]=1;
    for(int i=0; i<q; i++) {
        int op,x;
        scanf("%d%d",&op,&x);
        if(op==1) {
            if(!xd[x]) {
                xd[x]=1;
                floyed(n, x);
            }
        }
        else if(op==2) {
            if(!xd[x]||G[s][x]==INF)    printf("-1\n");
            else {
                printf("%d\n",G[s][x]);
                s=x;
            }
        }
    }
    return 0;
}

版权声明:本文为m0_51303687原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。