niop準備 題目分類

【sg函數】
new oj 1755 分裂遊戲 :
將每一個單獨的石子看成一個遊戲; sg(i)表示一個石子距離結束位置i 有堆石子遠 ;類比一個有向無環圖;每次可以從起點往後面走;這樣走到不能走的就輸了(sg[1]=0)//1=n-n+1;
這樣最後再把所有的sg^起來;
如: 1 4 3
sg[3]^sg[2]^sg[2]^sg[2]^sg[2]^sg[1]^sg[1]^sg[1]

【倍增法】
new oj 2347 國旗計劃 :
預處理出f[i][j] 表示第i個人用了2^j個區間後走到的區間,同時s[i][j]記錄這段路徑長度,查詢的時候類似樹上倍增求lca一樣往下枚舉j統計答案即可。

【半平面交】
new oj 2348 小凸想跑步 :
先把使得每個三角形和第一個三角形面積相等的直線求出,做一遍半平面交即可。

【網絡流】
new oj 1754 80人環遊世界 :
網絡流spfa+上下界。由於每個城市有一個固定的訪問量,故想到將城市拆分成兩個點,中間的連邊用上下界都是vis[i]來卡死。這樣其他邊不變,跑一遍spfa即可。

new oj 2405 orzwwx :
很顯然的網絡流,構圖很簡單(網絡流就這個難),然後裸地跑一邊無源匯的上下界。判斷是否可行。然後刪掉超級源點和超級匯點,(T->S的inf邊可刪可不刪)。再進行一次增廣看最多的解就可以了。

new oj 1546 導彈防禦塔 :
很明顯的網絡流,只是圖很難構建,因爲所有的防禦塔攻擊時間是可以相交的。所以怎麼辦呢?可以知道ans>=max*(each (t1*k+t2*(k-1)+dist[i][j]))對於每個攻擊對象的dist+總的冷卻時間+總的發射時間是<=ans的。所以就二分答案。然後每個防禦塔拆成m個點表示第幾個發射,這樣最後直接跑一邊二分圖最大匹配就好了。注意加邊不要重疊。一開始老是往修車那邊靠,結果爆0。其實網絡流24題裏面有些就是二分答案的,下次注意。

【dp】
new oj 2177 Sailing Race :
首先考慮不經過第一條邊的情況,那麼用f[i][j][0]表示順時針走從i出發第二步到j可以經過的最多路徑數;
逆時針同樣,轉移方程爲 f[i][j][0]=max(f[i][j][0],1+max(f[k][j][0],f[k][i][1]));
如果有經過第一條邊,則必定在一邊是貼着圓走然後經過第一次的路徑再到另一半的圓中去隨便走。後面的一部分第一問已經求了。所以再用t[i][j]表示從i到j貼着圓走能經過幾個點,然後則這個情況下讓分隔線儘量地靠近j點能使後一部分儘可能大。注意for的順序。

new oj 2404技能點(skillpoint) :
分析題目可得到這樣的性質:每個技能必定是儘量加滿的最優,也就是最後的情況一定是所有要求的技能+滿流技能+至多一個不滿的技能。
所以預處理把“至多一個不滿的技能”當成一個要求技能;注意這個很小可以狀壓。f[i][j]表示前i個技能j是滿足技能的狀壓;

for(int i=1;i<=num;i++)
    {
        now=i&1;
        pre=now^1;// 注意要用滾動數組節省空間;
        for(int j=0;j<=S;j++) f[now][j]=0;
        for(int j=0;j<L;j++)
        {
            int cnt=0;
            for(int t=1;t<=S;t++)
                if(j&(1<<(t-1))) f[now][j]=max(f[now][j],f[pre][j^(1<<(t-1))]+a[pos[i]][limit[t]]),++cnt;

            if(i>cnt) f[now][j]=max( f[now][j],f[pre][j]+(i-cnt<=all? a[pos[i]][k]:0));    
        }   
    } 

new oj 1275 股票交易 trade :
可以想到這樣的dp式子:
f[i][j]表示第i天剩餘j個股票獲得的最大收益。
f[i][j]=max(
f[i-1][j] , //昨天留下。
-j*inval[i],//今天剛買。
f[i-w-1][now]+(now-j)*inval[i]。// 上次交易+剛買;
f[i-w-1][now]+(now-j)*outval[i]。//上次交易-剛賣。
)
這樣話需要每個j都要枚舉now,用單調隊列搞一下就可以。
部分代碼:

 for(int i=1;i<=T;i++)
    {
        for(int j=0;j<=it[i];j++) f[i][j]=-j*il[i]; // 這個要加; 
        for(int j=0;j<=Maxp;j++) f[i][j]=max(f[i][j],f[i-1][j]);
        int pre=i-W-1;
        if(pre>=0)
        {
            head=1; tail=2;
            q[1]=0;
            for(int j=0;j<=Maxp;j++)
            {
                while(head<tail && q[head]<j-it[i]) head++;
                int now=q[head];
                if(head<tail)  
                f[i][j]=max(f[i][j],f[pre][now]+(now-j)*il[i]);
                while(head<tail && f[pre][j]+j*il[i]>f[pre][q[tail-1]]+q[tail-1]*il[i]) tail--;
                q[tail++]=j;
}

new oj 1110 bookshelf :
這種關於連續區間的dp一般是枚舉斷點,f[i]=f[j]+max(from h[j] to h[i])注意到從j到i中的高度是一個單調遞減的序列,如5,3,4中3不影響答案,因爲3可以併到5的情況中去,答案一定更優。
所以這裏用一個單調隊列維護一下,然後開一個大根堆就可以。

new oj 1770 nochange :
很顯然的dp,總的只有16個銀幣,所以狀壓一下隨便轉移就好了(考場上答案輸出-1我輸出0被扣30):

 for(int i=Max-1;i>=0;i--)
    {
        for(int j=1;j<=k;j++)
            if((i&(1<<(j-1)))==0)
            {
                int pre=i^(1<<(j-1));
                int x=get_pos(f[pre],coin[j]);//二分找x最大值。 
                f[i]=max(x,f[i]);   
            }   
    }

【數論】
new oj 2010 老人 :
首先考慮答案ans:分解質因數以後得到的是p1^k1*p2^k2……pn^kn; 對於一個p的貢獻是(1+p^1+p^2+p^3……p^k1)提出(1+p)(1+p^2+p^4+…)則(1+p)能被2^m整除,後一項提出(1+p^2)(p^4+p^6…)
(1+p^2)②能被2^t整除,則p=2^m-1代入②得2(2^(2n-1)-2^(n-1)+1)| 2^t; 顯然不滿足題意;因此ans分解質因數以後只有質因數的一次方且質因數滿足=2^k-1;爲2,3,5,7,13,17,19,31;所以可以把這幾個數狀壓起來;判斷每個讀入的數分解後是不是由這幾個數的一次方組成;然後再更新ans。

new oj 1933 墨墨的等式 :
對於這個多項式,首先容易想到的應該是答案爲ans(Bmax)-ans(Bmin-1) 然後觀察多項式寫成如下形式 a1*x+k=B(任意一個整數);k爲後面那些的結果(與0-a1-1一一對應) a1爲最小的係數(跑起來快)。如果能夠對於每一個的k,都能找到最小的x使得等式成立,那麼就可以求出方案數,所以把0-(a1-1)看成a1個點,每個點用a2-an進行連邊i->(a[j]+i)%a[1]連一條邊權爲a[j]的邊,那麼最後跑一邊最短路求出來的就是組成k的最小值,然後就可以

 Bmin--;
    for(int i=0;i<a[1];i++) if(dist[i]<=Bmax) ans1+=(LL)(Bmax-dist[i])/LL(a[1])+1;
    for(int i=0;i<a[1];i++) if(dist[i]<=Bmin) ans2+=(LL)(Bmin-dist[i])/LL(a[1])+1;
    printf("%lld\n",ans1-ans2);

【線段樹】
new oj 1710 覺醒力量 :
由於14689=11*13*17*19; 聯想到這個等式 f(x)%p==f(x+p)%p;
其中p是質數; 所以我們只要知道一個詢問對於11,13,17,19,的結果是多少;就可以用中國剩餘定理求出來。而對於每個數。每次計算會超時(插入的時候)。所以建立四顆線段樹分別預處理出所有的方案即可。
(中國剩餘定理的簡單計算)
{
如對於某個x;
被3除餘1,被4除餘2,被5除餘4,這個數最小是幾?
題中3、4、5三個數兩兩互質。
則〔4,5〕=20;〔3,5〕=15;〔3,4〕=12;〔3,4,5〕=60。
爲了使20被3除餘1,用20×2=40;
使15被4除餘1,用15×3=45;
使12被5除餘1,用12×3=36。
然後,40×1+45×2+36×4=274,
因爲,274>60,所以,274-60×4=34,就是所求的數。
}

【矩陣乘法】
new oj 1718 傑傑的女性朋友 ( travel ) :
本題中容易想到的一種解法就是對於矩陣a[i][j]表示從i到j走一步的方案數,然後用黑科技(外加鬼節點)來記錄中間的方案數跑一邊矩陣快速冪就可以。然而只有五十分(矩陣太大);所以想辦法;
首先發現每兩個city之間的路徑數要有k種計算,因此多加k個節點。向每兩個城市連邊。因此從i到j可以看成是i->k->j;而k中兩點所走的路徑是多少呢?k1->k2必然要經過別的城市,事實上是所有的城市,所以

for(int i=1;i<=K;i++)
        for(int j=1;j<=K;j++)
            for(int z=1;z<=n;z++)
                {
                    map[i][j]+=(LL)ind[z][i]*out[z][j];
                    if(map[i][j]>p) map[i][j]%=p;    
                }
// 而鬼節點的連邊是這樣的:
for(int i=1;i<tot;i++) mi[i][tot]=ind[T][i];
// 結果計算:
 for(int i=1;i<tot;i++) ans=(LL)(ans+base[i][tot]*out[S][i])%p;   

PS:注意+1-1的問題和當走0,1步的時候,這裏在考場上本來是50結果被卡了40分。

new oj 1813 reading :
很顯然的矩陣乘法,像這種方案數超級多顯然不能一個一個算而且要保存中間過程的當然首選矩陣。比較蛋疼的就是有一個差異值,然而發現差異值最多是5。所以進行拆點,每個點分爲五個點,矩陣構法是這樣的:

void build()
{
//f[i][j]如果沒有特殊要求爲1,有的話就是差異值。
    for(int i=0;i<imax;i++)
        for(int j=0;j<imax;j++)
        {
            int now=f[i][j];
            int nowi=i*5+now; int nowj=j*5+1;
            base[nowi][nowj]=1;
        }
    for(int i=0;i<imax;i++)
        for(int j=1;j<=4;j++)
        {// 每個字母的五個分身連邊。
            int nowi=i*5+j; int nowj=i*5+j+1;
            base[nowi][nowj]=1;
        }
    base[131][131]=1;//鬼節點
    for(int i=0;i<26;i++) base[i*5+1][131]=1;

    for(int i=1;i<=131;i++)
        for(int j=1;j<=131;j++) mi[i][j]=base[i][j]; 
}
//統計部分:
for(int i=0;i<26;i++) 
    { ans+=base[i*5+1][131]; if(ans>p) ans%=p;}

【差分約束系統】
new oj 1534 Intervals :
主要是普及算法題。如果知道了差分約束系統就很簡單。首先設一個f[i]表示Z集合中小於i的數有幾個。然後題目條件[l,r]中至少a個數可轉化爲:f[r]-f[l-1]>=a; 得到一系列方程組。轉化成圖論,最後求f[Max]-f[Min-1]的Max。跑一遍spfa即可。注意隱藏條件:0<=f[i+1]-f[i]<=1。
(差分約束系統)
{
如給出三個不等式,b-a<=k1,c-b<=k2,c-a<=k3,求出c-a的最大值,我們可以把a,b,c轉換成三個點,k1,k2,k3是邊上的權,如圖
這裏寫圖片描述
由題我們可以得知,這個有向圖中,由題b-a<=k1,c-b<=k2,得出c-a<=k1+k2,因此比較k1+k2和k3的大小,求出最小的就是c-a的最大值了。
}

【最短路】
new oj 1533 robot :
詳見亂射的圖論之神,二維spfa

【數形結合】
new oj 1464 defend :
首先可以推出這樣的公式:對於某一次需要的攻擊,設此時隊伍長度是len,前綴血量是sum
for(int i=len;i>=1;i--)
ans=max(ans,(sum[len]-sum[i-1])/(d[len]+(len-i)*d));

這樣枚舉的話是O(n^2)的,只能百分四十;但是可以通過觀察上式發現分子寫成(d[len]+len*d- i*d)因此看成是兩個點A(d[len]+len*d,sum[len])和B(i*d,sum[i-1])這樣求出的斜率最大就是答案,對於每個A,可能成爲答案的是下凸殼上的點,因此維護一個下凸殼,查找的時候發現斜率是一個單峯函數,用三分即可。

【KMP】
new oj 1062 sub :
這道題是Match的簡化版,只要在模板串上求一遍fail指針即可,然後從後往前枚舉,i的sum累加到fail[i]上(初始一開始sum爲1)在正向掃一遍就可以。具體可以看Match有以前的註解。

【行列式】
new oj 2405 orzcyr :
很關鍵的思維突破點在於題目中的不相交處理。考慮兩條路徑,如果把他們的焦點後的路徑反過來,也就是說A在焦點後走了B的路,反過來B走A的路,那麼最後答案抵消不影響。所以我們預處理出一個矩陣:s[i][j]存放i->j的方案數。然後對於每一種j的排列計算答案(每種方案數連乘)再相加即可(正負號要注意)因此我們注意到這和行列式幾乎就是一個東西。套用一下就可以。

int flag=1;
    for(int i=1;i<=tot;i++)
        for(int j=i+1;j<=tot;j++)
        {
            while(A[j][i])
            {
            // 注意這裏的消元方法,由於p不是一個質數,所以採用
            //了類似gcd的過程。由於交換兩行行列式的值會相反,
            //用flag體現。
                int cha=A[i][i]/A[j][i];
                for(int z=i;z<=tot;z++)
                {
                    A[i][z]=(A[i][z]-(LL)A[j][z]*cha)%p;
                    swap(A[i][z],A[j][z]);  
                }
                flag=-flag; 
            }

【Tarjan】
new oj 1536 network:
詳見亂射的圖論之神詳解,這裏不再贅述。

【莫隊】
new oj 1667 :
普及算法題,莫隊。

【思維雜題】
new oj 1179 高樓 skyscraper :
容易分析得出這樣的結論:最高的樓一定是看得見的;
如果我們能夠知道對於每一個最高的樓的位置的左右方案數;就能知道答案。
分析對於高樓在i位置。首先左邊高度有C(n-1,i-1)的選擇方式。然而有效的只有他們的相對高度。所以用f[i][j]表示i棟樓j個上升的方案數,下降是一樣的(倒着數)。所以得出:

f[i][j]=f[i-1][j-1]+(n-1)f[i-1][j];
// 每次考慮插入一個最小的,一種情況是插在最開頭,上升序列數+1,一種是插在(n-1)個位置,不影響上升數量(被擋住)。

所以只要求c[n-1][1]~c[n-1][n-1],和要求長度長度的f[1][L]~f[i][L](下降一樣)即可。最後統計式:

for(int i=l;i<=n-r+1;i++)
    ans+=((LL)c[i-1]*L[i-1]%Mod)*R[n-i];

new oj1365 fly :
像這種區間的取法很多時候是按照右端點排個序就ok,查詢和修改的時候用線段樹維護一下就好了(考場上自己定義了一個比較方式還70,講評後刪掉一段代碼加上一個sort就A了。)
至於爲什麼這樣排序是對的可以這樣理解:
每次我們都是使得車上的人儘量快的下車,好騰出空間給下一些人。具體可以自己畫圖理解,這裏不再贅述。

new oj 2407orzzcz :
這個題目一看我就一直向sg靠結果當然是錯的。首先從小的分析:

  • 只有一堆,先手必勝(全取);
  • 只有兩堆且一樣多,先手必敗。(無論一開始先手怎麼取,後手總能將這兩堆石子變成一樣多,也就是做對稱的操作);
  • 兩堆不一樣多,顯然就先手必勝。
  • 拓展:
    -奇數堆的時候,先手必勝。先手可以把最大堆的拿出來分配,將剩餘配成兩兩相等的若干組。則後手就輸了。
    -偶數堆的時候
    {
    若兩兩數量相等,先手必敗。
    else 先手可以拿出最大的一堆分配,不要全取完,可以使得最後還是每兩堆相等。先手必勝。
    }

new oj 1430 ballmachine :
這題的二操作可以把這條鏈上的第一個非空節點移到末尾即可,然後記錄一下長度就是移動的數量。有一個地方就是無論怎麼放都不會改變它的選擇順序,所以先用dfs預處理出這棵樹的選擇順序,(注意中間不能用一個數組暫存,不然dfs會更改)。然後把還沒有球的加入堆中,操作一一直取堆頭即可,操作二用倍增法求一下祖先就可以。

new oj 1232 思維+樹上最長鏈 :
通過觀察可以發現,如果把三角形A通過一系列相鄰的三角形能走到三角形B則AB一定存在一條線段經過它們中間所有的三角形。否則就是一個凹多邊形。所有把每個三角形看成一個點,三角形相鄰就在對應的點上進行連邊,最後求一個樹上最長鏈就可以。
樹上最長鏈的求法:
先從任意一個點進行BFS,然後選取最後入隊的那個點也就是使得u->v是從u出發的最長鏈,然後再從v出發做一遍BFS即可,具體證明可以自己用反證法進行證明。

mew oj 1626 zewi :
水題,直接字符串排序後找相鄰的最長公共前綴就行。考場上還SB的寫了一個後綴數組。

發佈了32 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章