1. 程式人生 > >4.1模擬題

4.1模擬題

貢獻 amp 需要 第一個 模板 名稱 元素 排序 刪除

期望得分:0+10+100

實際得分:0+0+100,rank 2

智乃
【題目描述】
給你N個字符串,你每次可以選擇其中一個字符串的一段前綴進行翻轉,但
是你必須保證這個前綴的長度是偶數。你可以進行無限次這樣的操作,並且如果
兩個字符串變得相同的時候,你就可以把這兩個字符串都刪除掉,問最後最少剩
下多少個字符串?
【輸入格式】
第一行一個整數T代表數據組數。
對於每組數據,第一行一個整數N代表字符串個數。
接下來N行每行一個字符串。
【輸出格式】
對於每組數據,一行一個整數代表答案。
【樣例輸入】
2
5
esprit
god
redotopc
odcpoter
dog
14
rats
live
stressed
to
act
as
star
desserts

of
evil
cat
sa
fo
ot

【樣例輸出】
3
0
【樣例解釋】
無。
【數據範圍與規定】
對於40%的數據,字符串長度不超過8。
對於100%的數據,1 ≤ T ≤ 11,字符串長度不超過50,1 ≤ N ≤ 50。

題解:我們手動模擬一下樣例,容易發現一個事實:

雖然我們可以翻轉無數多次,但是如果一個區間被翻轉了偶數次,那麽這個區間保持不變;如果一個區間被翻轉了奇數次,那麽這個區間就被翻轉過來了。即我們只需要考慮翻轉次數是奇數次還是偶數次即可。

每次操作只能翻轉一個長度為偶數的前綴意味著如果字符串長度為奇數的話,最後一個字符不可以被翻轉到除了第一個字符和最後一個字符的其他位置。

此外其余的字符串可以劃分為2個一組,對於每一組字符串的兩個字符位置可以互換,劃分出的字符串位置可以互換。

假設我們現在有一個字符串abcdefgh,我們要翻轉區間$[3,4]$,那麽我們可以先翻轉區間$[1,4]$,然後翻轉區間$[1,2]$。

如果我們要把區間$[1,2]$移動到$[5,6]$,我們可以先翻轉區間$[1,2]$,再翻轉區間$[5,6]$,再翻轉區間$[3,4]$,最後翻轉區間$[1,6]$。

即我們令$a,b,c,d\in\bf{Z}$,且假設兩個區間沒有交集:

假設要把區間$[2a-1,2b]$翻轉過來,我們可以先對區間$[1,2b]$進行一次翻轉,然後再對區間$[1,2a-2]$進行一次翻轉。

假設要把區間$[2a-1,2b]$移動到$[2c-1,2d]$,我們可以先對區間$[2a-1,2b]$進行一次翻轉,然後對區間$[2c-1,2d]$進行一次翻轉,然後再對區間$[\max(2a-1,2c-1),\min(2b-1,2d-1)]$進行一次翻轉,最後再對區間$[\min(2a-1,2c-1),\max(2b,2d)]$進行一次翻轉。

很明顯,對於其他操作,我們可以由上述兩個操作轉化而來。我們就證明出了翻轉後的字符串的可能情況。

另外我們還有一個發現,如果兩個字符串的長度不相等,那麽無論怎麽翻轉它們一定不可能相等。

證明:顯然。

由上所述,我們可以得到一個比較合理的算法:

對於每個字符串,我們都拆分為長度為2的子字符串,如果原字符串長度為奇數,則固定單個字符所拆分出的長度為2的字符串的第2個字符為‘\0’,對於每個拆分出來的字符串,我們保證字典序較小的字符總出現在字典序較大的字符之前,然後模擬翻轉區間操作,使得字典序較小的字符出現在字典序較大的字符之前,再湊出原來的字符串,把翻轉後的字符串扔到一個map裏面,最終答案就是map中每一個元素出現次數對2取模的值之和(明顯的,如果一個字符串出現了奇數次,那麽會剩下一個字符串無法被消除)。

麻耶
【問題描述】
油庫裏是幻想鄉特有的一種生物。每只油庫裏都有一個戰鬥力值和一個能量
值。當兩只油庫裏戰鬥時,總是戰鬥力值高的一位獲勝。獲勝者的戰鬥力值將變
成(自己的原戰鬥力值-對手的戰鬥力值+對手的能量值)。敗者將死去。若兩者戰
鬥力值一樣,則同歸於盡。
思考熊發現了很多油庫裏,他想知道通過互相戰鬥之後油庫裏中戰鬥力值+
能量值最高的一個可能到達多少。你能幫他們求出來嗎?(假設除了考察的那只
油庫裏之外,其他油庫裏之間不會發生戰鬥)
【輸入格式】
第一行是一個整數N,代表當前有多少油庫裏。
接下來的N行, 每一行有兩個整數u,v, 代表這只油庫裏的戰鬥力值和能量值。
【輸出格式】
輸出一個整數,代表油庫裏中戰鬥力值+能量值最高的一個能達到多少。
【樣例輸入】
2
1 2
2 1
【樣例輸出】
4
【樣例解釋】
無。
【數據規模與約定】
對於100%的數據,1 ≤ u,v ≤ 10^9。

數據點編號 N= 數據點編號 N=
1 2 6 14989
2 984 7 21726
3 6168 8 100000
4 10470 9 100000
5 19168 10 100000

題解:

我們考慮貪心的策略:定義一只油庫裏的貢獻值為它的能量值-它的戰鬥力,則對於每一只貢獻值為負數的油庫裏,就幹脆不要打(明顯的,任何一只油庫裏如果打這只油庫裏戰鬥力值+能量值會減少)。由此將油庫裏分成兩部分。

我們考察每一只油庫裏。

對於貢獻值為正數的油庫裏,我們將它們按照戰鬥力大小從小到大進行排序,那麽在所有貢獻值為正數的油庫裏中,戰鬥力最大的油庫裏的答案最大,最大答案為它打敗的所有油庫裏的貢獻和+它的能量值+它的戰鬥力-它的貢獻值,化簡之後是它打敗的所有油庫裏的貢獻和+2*它的戰鬥力。

我們考慮如何快速求出它打敗的所有油庫裏的貢獻和,預處理一個前綴和數組$pre[i]$表示打敗區間$[1,i]$之間的油庫裏能獲得的貢獻值即可。

對於貢獻值為負數的油庫裏,我們只需要按照戰鬥力從大到小進行排序,那麽同理,在貢獻值為負數的油庫裏中,戰鬥力最大的油庫裏的答案最大。由剛開始的分析我們容易看出,我們不能打貢獻值為負數的油庫裏,這只油庫裏只應該打貢獻值為正數的油庫裏。

我們考慮如何求出這只油庫裏能夠打敗多少只貢獻值為負數的油庫裏。

維護一個數組$dp[i]$表示如果要獲得第$i$只油庫裏的貢獻值所需要的最小初始戰鬥力是多少,我們容易得到遞推方程$dp[i]=\max(dp[i-1],第i只油庫裏的戰鬥力-pre[i-1])$(第$i$只油庫裏至少需要打敗前一只油庫裏才能獲得貢獻值)。

在處理每只貢獻值為負數的油庫裏時,我們在$dp$數組裏二分查找油庫裏的初始戰鬥力,然後到$pre$數組裏去查詢能獲得的貢獻值即可。

最後在這兩種油庫裏的答案之間取一個最大值即可。


【問題描述】
現在你要實現一個文件系統,支持以下操作
cd Directory_Name
如果當前路徑下有名為 Directory_Name 的文件夾,則進入該文件夾所對應
的路徑,否則輸出“No such directory!” 。
cd ..
如果當前路徑存在父路徑, 則返回父路徑, 否則輸出 “No parent directory!” 。
touch File_Name
如果當前目錄下存在名為 File_Name 的文件則輸出“File already exists!” ,
否則創建這樣一個文件。
rm File_Name
如果當前目錄下存在名為 File_Name 的文件則刪除它,否則輸出“No such
file!” 。
mkdir Directory_Name
如果在當前路徑下存在名為 Directory_Name 的文件夾,則輸出“Directory
already exists!” ,否則創建這樣一個文件夾(當前路徑不變) 。
rmdir Directory_Name
如果在當前路徑下存在名為 Directory_Name 的文件夾,則刪除之,否則輸
出“No such directory!” 。
ls
列出當前路徑下所有的文件和文件夾,每一項占一行,按創建的先後順序給
出。采用以下形式輸:
“Item_Name Type”
Type 為 <D>(文件夾)或<F>(文件)
註意:同一路徑下文件與文件夾可以同名,但同一路徑下文件與文件、文件
夾與文件夾不能同名。
初始時當前路徑處於根路徑下,無父路徑。
【輸入格式】
第一行為Q,表示有Q個操作。
接下來是Q行,每行輸入為以上描述的操作之一。

【輸出格式】
輸出答案。
【樣例輸入】
3
mkdir standy
touch totalfrank
ls
【樣例輸出】
standy <D>
totalfrank <F>
【樣例解釋】
無。
【數據規模與約定】
對於100%的數據,1 ≤ Q ≤ 100,所有文件名字長度不超過200且均為小寫
字母。

題解:

這應該是這三道題目中最簡單的一道了。因為數據規模很小,所以順著性子模擬就行了。

我的思路是開一個數組$tree[i]$表示創建時間為$i$的文件/文件夾的信息,當然不需要記錄文件的父親,因為不會cd到一個文件下面。然後對於每一個節點,我們存儲兩個map,模板分別為<int,pair<string,char> ><pair<string,char>,int>,分別對應修改時間->文件信息和文件信息->修改時間的映射。然後每次操作分別維護兩個map和時間戳即可。創建文件夾時還需要維護它的父親目錄的創建時間。對於根目錄,我們把它的父親目錄設置為一個無效的值然後在切換目錄時判斷一下就行了。

然後就是一個大模擬了。

#include<map>
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
typedef map<int,pair<string,char> > isc;//我不要寫很長的一坨模板名 
typedef map<pair<string,char>,int> sci;
struct DICT{
    int par;//父親節點的創建時間(數組下標) 
    sci f;//當前目錄下xx名稱+類型->目錄下xx的創建時間(數組下標)。 
    isc t;//當前目錄下xx創建時間(數組下標)->目錄名稱+類型。 
};
DICT tree[123456];//目錄樹
int now;//當前目錄的數組下標 
int timestamp=1;//時間戳 
const int INVALID_PAR = 233333;//我們規定,當par==233333時,表示是根節點。
sci::iterator ret;//避免每次都創建一個叠代器 
int q;
string opt,na;
//剩下的就是模擬了233333... 
inline void cd(string par)
{
    if(par=="..")
    {
        if(tree[now].par==INVALID_PAR)cout<<"No parent directory!\n";
        else now=tree[now].par;
    }
    else
    {
        ret=tree[now].f.find(make_pair(par,‘D‘));
        if(ret==tree[now].f.end())cout<<"No such directory!\n";
        else now=ret->second;
    }
}
inline void touch(string name)
{
    ret=tree[now].f.find(make_pair(name,‘F‘));
    if(ret!=tree[now].f.end()){cout<<"File already exists!\n";return;}
    tree[now].f[make_pair(name,‘F‘)]=timestamp;
    tree[now].t[timestamp]=make_pair(name,‘F‘);
    ++timestamp;
}
inline void rm(string name)
{
    ret=tree[now].f.find(make_pair(name,‘F‘));
    if(ret==tree[now].f.end()){cout<<"No such file!\n";return;}
    int pos=ret->second;
    tree[now].f.erase(ret);
    tree[now].t.erase(pos);
}
inline void mkdir(string name)
{
    ret=tree[now].f.find(make_pair(name,‘D‘));
    if(ret!=tree[now].f.end()){cout<<"Directory already exists!\n";return;}
    tree[now].f[make_pair(name,‘D‘)]=timestamp;
    tree[now].t[timestamp]=make_pair(name,‘D‘);
    tree[timestamp].par=now;
    timestamp++;
}
inline void rmdir(string name)
{
    ret=tree[now].f.find(make_pair(name,‘D‘));
    if(ret==tree[now].f.end()){cout<<"No such directory!\n";return;} 
    int pos=ret->second;
    tree[now].f.erase(ret);
    tree[now].t.erase(pos);
}
inline void ls()
{
    for(isc::iterator it=tree[now].t.begin();it!=tree[now].t.end();++it)
        cout<<it->second.first<<" <"<<it->second.second<<">\n";
}
int main()
{
    tree[0].par=INVALID_PAR;
    #ifndef LOCAL
    freopen("nacumegu.in","r",stdin);
    freopen("nacumegu.out","w",stdout);
    #endif
    cin>>q;
    while(q--)
    {
        cin>>opt;
        if(opt!="ls")cin>>na;
        if(opt=="cd")cd(na);
        else if(opt=="touch")touch(na);
        else if(opt=="rm")rm(na);
        else if(opt=="mkdir")mkdir(na);
        else if(opt=="rmdir")rmdir(na);
        else ls();
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

垃圾代碼,跑的賊慢。

4.1模擬題