牛客IOI周赛28-普及组
String Game
简单模拟,把第一个移到末尾,移动的次数x不超过n,直接从第x个字符开始输出,然后输出前面的数
类似的如果超过n个字符,每移动n个字符,相当于还是原字符串,所以每次从开始输出,再输出
的数
#include<cstdio> #include<iostream> #include<cstring> using namespace std; long long n,x; string s; int main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n>>x; cin>>s; x%=n; for(int i=x;i<n;++i) cout<<s[i]; for(int i=0;i<x;++i) cout<<s[i]; return 0; }
Sequence Game
最长上升子序列(LIS)的变式
其实就是二维版LIS
定义为前i个数,取值为第j个值的LIS
可以易得
直接写出来O(10^12)直接裂开,考虑降维(打击)i维
这里的还有贪心的一点
对于相同的长度即,末尾越小越好,
所以我们可以造一个数组意为LIS长度为i的最小结尾数
第i行,把与不同长度末尾比较,大了就可以连接成一个更长的LIS
然后还要考虑是否能更新
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int inf=1e9; const int maxn=5e3+10; int a[maxn]; int ed[maxn];//存储长度为i,结尾最小的a的值 int dp[maxn];//记录以a[i][j]结尾的最长长度 int k,n; int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>k>>n; for(int i=1;i<=n;++i) ed[i]=inf;//初始化 for(int i=1;i<=n;++i){ for(int j=1;j<=k;++j) cin>>a[j]; int p=0; for(int j=1;j<=k;++j){ while(p<n && ed[p+1]<a[j]) ++p;//把a[j]和不同长度的末尾比较 dp[j]=p+1;//此时以a[j]为结尾的最长上升子序列为p+1 } for(int j=1;j<=k;++j) ed[dp[j]]=min(ed[dp[j]],a[j]);//更新以dp[j]长度为结尾的,是选择a[j]为结尾,还是原数为结尾 } for(int i=n;i>=1;--i){//这个长度有值,则输出 if(ed[i]!=inf){ cout<<i<<endl; break; } } return 0; }
Simple Game
题目背景没有鸟用,直接看输出描述每个地铁站能到达的编号最小的地铁站的编号
反向建图,那么每个点能到达,那么这样就意味着,每个点遍历时,直接把能到这个点的所有点都更新为这个点的编号,从编号1开始搜索即可,搜过的打标记不用搜
因为第一次被搜到的点记录的一定为最小值
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n; const int maxn=1e5+10; int m; bool book; int head[maxn]; struct node{ int v,next; }e[maxn]; int nr[maxn]; int cnt=0; int in[maxn]; void add(int u,int v){ e[++cnt].v=v; e[cnt].next=head[u]; head[u]=cnt; } void dfs(int u,int topf){ nr[u]=topf; for(int i=head[u];i;i=e[i].next) if(nr[e[i].v]==0)dfs(e[i].v,topf); } int main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n>>m;memset(nr,0,sizeof(nr)); for(int i=1;i<=m;++i){ int u,v;cin>>u>>v; add(v,u); } for(int i=1;i<=n;++i) if(nr[i]==0)dfs(i,i); sort(nr+1,nr+1+n); int last=0;bool book=0; for(int i=1;i<=n;++i){ if(nr[i]==last) continue; last=nr[i];book=1; cout<<nr[i]<<" "; }if(book==0) cout<<1<<endl; return 0; }
Sweet Game
虽然是个取值题,但实质是一个构造题,构造一个序列使得值最大,构造时就求值,不用求出序列
思路
直接将第n个糖加入序列(因为它右边的糖无糖可吃,满足题意直接加)
那么此时n-1个糖也满足加入序列的条件(因为第n个糖吃完了)
以此类推,所以从后往前枚举,会使得i+1~n的糖都被吃了,第i个糖也满足被吃条件
而加入序列,根据题意则会有两种情况
设a[]为糖的甜度,b[]为糖的变化值
加入整个序列的左边(说明i+1~n的糖将要吃完)
此时加入整个序列的右边(说明i+1~n的糖已经吃完)
此时
其实为什么不能从1开始吃
会发现先吃了第1颗糖之后,只能吃第 ~
颗糖
那么接下来只能吃2颗糖,吃完后,又只能吃第 ~
颗糖
以此类推,构造出来的序列是唯一的,所以很大可能不是解
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int n; const int maxn=2e5+10; long long ans=0; long long a[maxn],b[maxn]; inline long long read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return x*f; } int main(){ n=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=n;++i) b[i]=read(); for(int i=n;i>=1;--i) { long long nxt=max(ans+sum+a[i],ans+a[i]+(n-i)*b[i]);//ans+sum+a[i]为插入整个序列左边的情况,ans+a[i]+(n-i)*b[i]为插入右边 ans=nxt; sum+=b[i];//记录当前序列的整体▲d } cout<<ans<<endl;return 0; }