1. 程式人生 > 其它 >牛客IOI周賽28-普及組

牛客IOI周賽28-普及組

牛客IOI周賽28-普及組

String Game

簡單模擬,把第一個移到末尾,移動的次數x不超過n,直接從第x個字元開始輸出,然後輸出前面的數

類似的如果超過n個字元,每移動n個字元,相當於還是原字串,所以每次從\(x%n\)開始輸出,再輸出\(x%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
定義\(dp[i][j]\)為前i個數,取值為第j個值的LIS
可以易得
\(dp_{i,j}=max(dp_{x,y}+1) (a[i][j]>a[x][y],x<=i,y<=k)\)

直接寫出來O(10^12)直接裂開,考慮降維(打擊)i維

這裡的還有貪心的一點
對於相同的長度即\(dp[i][j]\),末尾越小越好,
所以我們可以造一個數組\(ed[i]\)意為LIS長度為i的最小結尾數

第i行,把\(a[j]\)與不同長度末尾比較,大了就可以連線成一個更長的LIS
然後還要考慮是否能更新\(ed[]\)

#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[]\)為糖的變化值

  1. 加入整個序列的左邊(說明\(i+1\) ~ \(n\)的糖將要吃完)
    此時\(ans=\sum_{j=i}^{n}b[i]+ans+a[i]\)

  2. 加入整個序列的右邊(說明\(i+1\) ~ \(n\)的糖已經吃完)
    此時\(ans=ans+a[i]+(n-i)*b[i]\)

其實為什麼不能從1開始吃
會發現先吃了第1顆糖之後,只能吃第\(2\) ~ \(n\)顆糖
那麼接下來只能吃2顆糖,吃完後,又只能吃第\(3\) ~ \(n\)顆糖
以此類推,構造出來的序列是唯一的,所以很大可能不是解

#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;
}

賽後總結

1.讀清題意
2.不要死磕一道題,實在磕不出來趕緊換
3.嘗試將考場題轉化為做過的題