20181020模擬賽 (1.貪心+2.樹dfs序+3.DP單調隊列優化+4.模擬)

Problem 1 嚎叫響徹在貪婪的廠房
程序名:factory.c/cpp/pas
時間限制:1s
空間限制:256M
RX:把機器人Hobo,帶往改造工廠。
來到了改造工廠,Hobo 感到陣陣迷茫,不知道自己將會何去何從。
“當我從這裏離開的時候,我還會是Eddie 的朋友嗎?”
RX:珍娜女王,可以開始了。
鐵斯塔:珍娜女王,等到把Hobo 改造完了,你父親的遺願就能實現了。
【問題描述】
機器人的哀嚎傳遍了整座工廠,於是,Hobo 決定帶着他們一起逃離這裏。工
廠的傳送帶上依次排列着N 個機器人,其中,第i 個機器人的質量爲Ai。Hobo
經過仔細觀察,發現:
1.來自同一個家族的機器人,在這N 個機器人中一定是連續的一段。
2.如果從第i 個機器人到第j 個機器人都來自同一個家族,那麼Ai 到Aj 從
小到大排序後一定是公差大於1 的等差數列的子序列。
Hobo 發現,不同家族的個數越少,機器人就會越團結,成功逃離工廠的概率
就會越高。Hobo 想知道,這N 個機器人最少來自幾個不同的家族呢?
【輸入格式】
第一行一個正整數N。
接下來一行N 個正整數,第i 個正整數爲Ai。
【輸出格式】
一行一個正整數,表示答案。
【樣例輸入1】
7
1 5 11 2 6 4 7
【樣例輸出1】
3
【樣例說明1】
1 5 11 是等差數列{1,3,5,7,9,11}的子序列,
2 4 6 是等差數列{2,4,6,8}的子序列,
7 是等差數列{7,9,11}的子序列。
【樣例輸入2】
8
4 2 6 8 5 3 1 7
【樣例輸出2】
2
【樣例說明2】
2 4 6 8 是等差數列{2,4,6,8}的子序列,
1 3 5 7 是等差數列{1,3,5,7}的子序列。
【數據範圍】
20%的數據滿足,N≤10。
40%的數據滿足,N≤100。
60%的數據滿足,N≤1000,1≤Ai≤10^6。
另有20%的數據滿足,Ai 互不相同。
100%的數據滿足,N≤100000,1≤Ai≤10^9。
Problem 2 主僕見證了Hobo 的離別
程序名:friendship.c/cpp/pas
時間限制:1s
空間限制:256M
最終,Hobo 還是沒能逃出改造工廠。傳送帶滾滾向前,把Hobo 送入無盡的黑暗。
與此同時,Millie 家中。
Eddie: Hobo 在哪?
Millie: 他被帶去“機器人改造計劃”了。
Eddie: 機器人改造計劃是什麼?Hobo 會變成什麼樣?
Millie: ……
五天後。
Eddie: Hobo,你可終於回來了,我……
Hobo: 我是A-01 型工作機器人,請主人吩咐工作。
【問題描述】
Eddie 終於意識到了改造計劃的本質,恨自己沒能阻止這一切的發生。在
Millie 和Simon 的幫助下,他拆開了Hobo 的電路板,決定幫助Hobo 找回記憶。
一開始,Hobo 的中央處理器有N 個元件(編號爲1 到N),每一個元件都存
儲了一段時間的記憶。也就是說,每個元件都可以看做一個集合。爲了喚醒Hobo
的回憶,他會時不時地在當前的所有元件中選擇K 個進行一次融合,組成一個新
的元件,新元件的編號等於融合之前元件的總個數加一。當然,參與融合的K
個元件融合之後依然存在,並且每個元件至多參與一次融合。
由於元件的容量有限,Eddie 沒有能力喚醒Hobo 全部的回憶,所以他會用下
列兩種方式來融合元件:
1.集合的交:一段記憶存儲在新的元件中,當且僅當這段記憶在參與融合的
K 個元件中都有儲存。
2.集合的並:一段記憶存儲在新的元件中,當且僅當這段記憶在參與融合的
至少一個元件中有儲存。
在融合元件的過程中,Eddie 迫切地想知道:凡是存儲在X 號元件中的記憶,
是否一定也存儲在Y 號元件中?
【輸入格式】
第一行兩個整數N,M。
接下來M 行,表示有M 種操作:
0 op K A1 A2 … AK 表示將A1,A2,…,AK 合成爲一個新的元件,op=0 表示
融合的方式是集合的交,op=1 表示融合的方式是集合的並。
1 X Y 表示一組詢問,X,Y 的意義參見問題描述。保證X 和Y 都不會超過當
前元件的總個數。
【輸出格式】
對於每個詢問,輸出一行一個0 或1,1 表示一定存儲在Y 號元件中,否則
用0 表示。
【樣例輸入】
3 5
0 0 2 1 2
1 1 4
0 1 2 3 4
1 4 5
1 4 2
【樣例輸出】
0
1
1
【樣例說明】
如果存在這樣一段記憶,它存儲在1 號元件中,但是它不存儲在2 號元件中,那麼它就
不會存儲在4 號元件中,所以第一個詢問的答案爲0。
而對於任意一段存儲在4 號元件中的記憶,它必然同時存儲在2 號和5 號元件中,所以
第二、第三個詢問的答案均爲1。
【數據範圍】
測試點N= M= ΣK≤ 備註
1 2000 2000 2000 K=1
2 2000 2000 2000 K=1
3 2000 2000 2000 前1999 個操作均不含詢問操作
4 2000 2000 2000 前1000 個操作不含詢問操作,
後1000 個操作都是詢問操作。
5 5000 5000 10000 只存在”集合的交”和詢問操作
6 5000 5000 10000 只存在”集合的交”和詢問操作
7 5000 5000 10000 只存在”集合的並”和詢問操作
8 100000 100000 200000 所有的詢問操作中X≤Y
9 100000 100000 300000 所有的詢問操作中X≥Y
10 250000 250000 500000 無
Problem 3 征途堆積出友情的永恆
程序名:empire.c/cpp/pas
時間限制:1s
空間限制:256M
Millie:Hobo,機器人和人類本來相處得這麼好,爲什麼珍娜女王要下令改造機
器人啊?
Hobo:她的父親,捨身救起了一個正在施工的機器人,自己卻被壓在了起重機下,
再也沒能醒來。父親死後,鐵斯塔蠱惑她,說:“機器人是沒有靈魂的,應該把
他們改造成人類的奴僕,這也是爲了你的父親。”
Millie:怎麼會這樣……
【問題描述】
爲了說服珍娜女王停止改造計劃,Eddie 和Hobo 踏上了去往王宮的征程。
Sunshine Empire 依次排列着(N+1)座城市,0 號城市是出發地,N 號城市是王宮。
火車是Sunshine Empire 的主要交通工具。Eddie 和Hobo 可以在當前的城市
上車,並且在之後的某一座城市下車。從第(i-1)座城市乘坐到第i 座城市需要
花費Ai 的費用。同時,在第i 座城市上車需要繳納Bi 的稅款。其中,稅款屬於
額外支出,不屬於乘坐這段火車的費用。
珍娜女王爲了促進Sunshine Empire 的繁榮發展,下令:如果連續地乘坐一
段火車的費用大於這次上車前所需繳納的稅款,則這次上車前的稅款可以被免
除,否則免去乘坐這段火車的費用。
然而,爲了保證火車的正常運行,每一次乘坐都不能連續經過超過K 座城市
(不包括上車時所在的城市),並且,Eddie 和Hobo 的移動範圍都不能超出
Sunshine Empire。Eddie 想知道,到達王宮的最小總花費是多少?
【輸入格式】
第一行兩個整數N,K,意義同題目描述所示。
接下來一行N 個整數,第i 個整數爲Ai。
接下來一行N 個整數,第i 個整數爲B(i-1)。
【輸出格式】
一行一個整數,表示總花費的最小值。
【樣例輸入1】
4 2
4 3 2 1
1 2 4 4
【樣例輸出1】
11
【樣例說明1】
第一段火車經過0,1,2 號城市,由於4 + 3 > 1,所以稅款免除,花費爲7。
第二段火車經過2,3,4 號城市,由於2 + 1 < 4,所以乘坐火車的費用免除,花費爲4。
總費用爲7 + 4 = 11。
【樣例輸入2】
4 2
4 3 2 1
1 2 10 3
【樣例輸出2】
12
【樣例說明2】
第一段火車經過0,1 號城市,花費爲4。
第二段火車經過1,2,3 號城市,花費爲5。
第三段火車經過3,4 號城市,花費爲3。
總費用爲4 + 5 + 3 = 12。
【數據範圍】
20%的數據滿足,N≤30,K≤5。
50%的數據滿足,N≤10000,K≤1000。
另有10%的數據滿足,Bi = 1。
另有10%的數據滿足,Ai = B(i-1)。
另有10%的數據滿足,N = K。
100%的數據滿足,N≤500000,K≤100000,1≤Ai,Bi≤10^6。

T1

貪心,最長連續的公共gcd大於1,且兩兩互不相同的最大劃分。
爲什麼貪心是對的呢,這麼想,對於某個元素a[i]a[i]來說,它既可以作爲前面往後的劃分,也可以單獨往後劃分,但無論前後沒有它,也都可以存在,所以可以貪心。
兩兩互不相同則用map維護(或者你閒的話打個離散化也可以)

# include <bits/stdc++.h>
# define Rint register int 
# define int long long
using namespace std;
const int MAXN=1e5+10;
int n,a[MAXN];
map<int,int>mp;
inline int gcd(Rint a,Rint b){ return !b?a:gcd(b,a%b); }
signed main()
{
//  freopen("factory.in","r",stdin);
//  freopen("factory.out","w",stdout);
    int n,ans=0; scanf("%lld",&n);
    for (Rint i=1;i<=n;i++) scanf("%lld",&a[i]);
    int l=1;
    while (l<=n) {
        int r=l+1; 
        int g=abs(a[r]-a[l]);
        mp[a[l]]=1;
        while (r<=n&&(gcd(abs(a[r]-a[l]),g)!=1)&&(mp.count(a[r])==0)) mp[a[r]]=1,r++,g=gcd(g,abs(a[r]-a[l]));
        r--; l=r+1; ans++; mp.clear();
    }
    printf("%lld\n",ans);
    return 0;
}

T2

考場可能有點不在狀態沒發現是顆樹,
於是強行構圖暴力,這麼構圖
譬如一些元素相交所產生新的點,那麼把新的點往這些老點連單向邊,(新點包含信息肯定舊點都有)
相併反之,
k=1時建立雙向邊
求x—>y是否存在路徑即可,
不知道是樹,所以直接dfs,知道以後則,
lca,這個就不說了
dfs序:對於x,y來說,若x和y的DFS區間不存在包含關係,則答案爲0。反之爲1。

#include<bits/stdc++.h>
using namespace std;
#define N 2100000

int dfn[N], last[N], f[N][2], low[N], du[N], a[N];
int dep[N], n, m, tot, x, y, t;
int op, k, i, j, u, v, cnt, tim;

struct edge { int v, next; } e[N]; 
struct query { int x, y; } Q[N];

void add(int u, int v){
  e[++cnt] = (edge){ v, last[u] };
  last[u] = cnt;
}

void dfs(int x){
  dfn[x] = ++tim;
  for (int i = last[x]; i; i=e[i].next){
    if (a[x] == 0 || du[x] == 1)
      f[e[i].v][0] = f[x][0] + 1;
    else
      f[e[i].v][0] = 0;
    if (a[x] == 1 || du[x] == 1)
      f[e[i].v][1] = f[x][1] + 1;
    else
      f[e[i].v][1] = 0;
    dep[e[i].v] = dep[x]+1;
    dfs(e[i].v);
  }
  low[x] = tim;
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(NULL);
  cin >> n >> m;
  tot = 0;
  for (int i = 1; i <= m; ++i) {
    cin >> t;
    if(t == 0) {
      cin >> op >> k;
      ++n;
      for (int j = 1; j <= k; ++j) {
        cin >> u;
        add(n, u);
      }
      a[n] = op;
      du[n] = k;
    } else {
      cin >> x >> y;
      Q[++tot] = (query){ x, y };
    }
  }
  for (int i = n; i >= 1; --i) if (!dfn[i]) dfs(i);
  for (int i = 1; i <= tot; ++i) {
    u = Q[i].x;
    v = Q[i].y;
    if (dfn[u] <= dfn[v] && low[v] <= low[u]) {
      if (f[v][0] >= dep[v] - dep[u]) puts("1");
      else puts("0");     
    } else if (dfn[v] <= dfn[u] && low[u] <= low[v]) {
      if (f[u][1] >= dep[u] - dep[v]) puts("1");
      else puts("0");      
    } else puts("0");
  }
  return 0;
}

T3
額,對於DP的優化我不是很會,所以直接暴力DP,然後特判了兩種情況,50.
果然……
單調隊列優化DP,
令sum[i]表示a[i]的前綴和。
易得狀態轉移方程:f[i]=min(f[j]+max(sum[i]sum[j],b[j+1]))ikji1f[i] = min(f[j] + max(sum[i] - sum[j],b[j + 1])),其中 i - k ≤ j ≤ i - 1。
注意到對於任意的j,f[j]sum[j]+sum[i]f[j]-sum[j]+sum[i]ii單調增,而f[j]+b[j+1]f[j]+b[j+1]是定值。
所以考慮使用堆來優化DP。
建兩個小根堆,一個維護f[j]+b[j+1]f[j]+b[j+1],另一個維護f[j]sum[j]f[j]-sum[j],則答案爲min(第一個堆的最小值,第二個堆的最小值 + sum[i])。
如果取到的j小於i-k,則彈出堆頂繼續取。
在第一個堆中取數時,如果發現它比f[j]sum[j]+sum[i]f[j]-sum[j]+sum[i]小,則把j加進第二個堆,彈出第一個堆的堆頂繼續取,直到取到符合條件的爲止。
顯然,一個j至多進堆2次,出堆2次。

#include<bits/stdc++.h>
using namespace std;
const long long inf=0x7ffffffffff;
int n,k,a[500010],b[500010];
long long f[500010],sum[500010];
inline int read()
{
    int x=0;bool w=false;char c=0;
    while(c>'9'||c<'0') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return w?-x:x;
}
struct data
{
    int j;long long val;
    bool operator < (const data &x) const{return val>x.val;}
};
priority_queue<data> q1,q2;
int main()
{
    n=read(),k=read();
    sum[0]=0;
    for(int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+(long long)a[i];
    for(int i=1;i<=n;i++) b[i-1]=read();
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
    {
        q1.push((data){i-1,f[i-1]+b[i-1]});
        while(!q1.empty())
        {
            data nw=q1.top();
            if(i-nw.j>k) q1.pop();
            else break;
        }
        while(!q2.empty())
        {
            data nw=q2.top();
            if(i-nw.j>k) q2.pop();
            else break;
        }
        while(!q1.empty())
        {
            data nw=q1.top();
            if(nw.val>f[nw.j]+sum[i]-sum[nw.j]) break;
            q1.pop();
            q2.push((data){nw.j,f[nw.j]-sum[nw.j]});
        }
        while(!q1.empty())
        {
            data nw=q1.top();
            if(i-nw.j>k) q1.pop();
            else break;
        }
        while(!q2.empty())
        {
            data nw=q2.top();
            if(i-nw.j>k) q2.pop();
            else break;
        }
        long long tmp=inf;
        if(!q1.empty()) tmp=min(tmp,q1.top().val);
        if(!q2.empty()) tmp=min(tmp,q2.top().val+sum[i]);
        f[i]=tmp;
    }
    printf("%lld",f[n]);
    return 0;
}

T4
這題真是rz題
我心態崩了
我們學校機房1有毒,一模一樣的程序,我在那裏把樣例和手測數據全過了,結果到了競賽教室,樣例都過不了???tmd所以沒查出錯
很簡單,我的方法,相同長度計入一個vector,一遍掃過去,對於最極限的b[len][j]b[len][i]&lt;=mb[len][j]-b[len][i]&lt;=m,以ii爲開頭的序列長度就是 jij-i

#include<bits/stdc++.h>
using namespace std;

char s[300010];
int a[300010],n,m;
vector<int> b[30];
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
      {
        scanf("%s",s);
        b[strlen(s)].push_back(i);
        //cout<<a[i]<<' ';
      }
      long long ans=0;
    for (int len=1; len<=20; len++)
      {
        int i=0,j=0;
        while (i<b[len].size())
          {
            while (b[len][j+1]-b[len][i]<=m&&j+1<b[len].size())//&&(j<b[len].size())) 
                  j++;
            ans+=j-i;
            //cout<<i<<' '<<j<<endl;
            i++;
          }
      }
    printf("%lld",ans);
    
}

好了,今天預估 100+70+50+100
實際 0+70+50+0
嘔嘔嘔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章