1. 程式人生 > >湖南集訓 Day 8

湖南集訓 Day 8

入陣曲(rally)

【題目描述】
丹青千秋釀,一醉解愁腸。
無悔少年枉,只願壯志狂。
小 F 很喜歡數學,但是到了高中以後數學總是考不好。
有一天,他在數學課上發起了呆;他想起了過去的一年。一年前,當他初識演算法競賽的
時候, 覺得整個世界都煥然一新。 這世界上怎麼會有這麼多奇妙的東西?曾經自己覺得難以
解決的問題,被一個又一個演算法輕鬆解決。
小 F 當時暗自覺得,與自己的幼稚相比起來,還有好多要學習的呢。
一年過去了,想想都還有點恍惚。
他至今還能記得,某天晚上聽著入陣曲,激動地睡不著覺,寫題寫到雞鳴時分都興奮不
已。也許,這就是熱血吧。
也就是在那個時候, 小 F 學會了矩陣乘法。 讓兩個矩陣乘幾次就能算出斐波那契數列的
第 10 100 項,真是奇妙無比呢。
不過,小 F 現在可不想手算矩陣乘法——他覺得好麻煩。取而代之的,是一個簡單的小
問題。 他寫寫畫畫, 畫出了一個 ? × ? 的矩陣, 每個格子裡都有一個不超過 ? 的正整數。
小 F 想問問你,這個矩陣裡有多少個不同的子矩形中的數字之和是 ? 的倍數?
如果把一個子矩形用它的左上角和右下角描述為 (? 1 ,? 1 ,? 2 ,? 2 ),其中? 1 ≤ ? 2 ,? 1 ≤ ? 2 ;
那麼,我們認為兩個子矩形是不同的,當且僅當他們以 (? 1 ,? 1 ,? 2 ,? 2 ) 表示時不同;也就是
說,只要兩個矩形以 (? 1 ,? 1 ,? 2 ,? 2 ) 表示時相同,就認為這兩個矩形是同一個矩形,你應該
在你的答案裡只算一次。
【輸入格式】
從檔案 rally.in 中讀入資料。
第一行,包含三個正整數 ?,?,?。
輸入接下來 ? 行,每行包含 ? 個正整數,第 ? 行第 ? 列表示矩陣中第 ? 行第 ? 列
中所填的正整數 ? ?,? 。
【輸出格式】
輸出到檔案 rally.out 中。
輸入一行一個非負整數,表示你的答案。
【樣例 1 輸入】
2 3 2
1 2 1
2 1 2
【樣例 1 輸出】
6
【樣例 1 說明】
這些矩形是符合要求的:
(1, 1, 1, 3),(1, 1, 2, 2),(1, 2, 1, 2),(1, 2, 2, 3),(2, 1, 2, 1),(2, 3, 2, 3)。
【樣例 2】
見選手目錄下的 rally/rally2.in 與 rally/rally2.ans 。
【資料範圍與約定】
子任務會給出部分測試資料的特點。如果你在解決題目中遇到了困難,可以嘗試只解
決一部分資料。
每個測試點的資料規模及特點如下表:


這裡寫圖片描述

//55 分 N^4 暴力程式碼
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 405
typedef long long LL;
int a[MAXN][MAXN];
LL sum[MAXN][MAXN];
inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while
(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f; } int main(int argc,char *argv[]){ int n,m,k; read(n),read(m),read(k); for(int i=1; i<=n; ++i) for(int j=1; j<=m; ++j){ read(a[i][j]); sum[i][j] = sum[i-1][j] + a[i][j] + sum[i][j-1] - sum
[i-1][j-1]; } int Ans = 0; for(int a=1; a<=n; ++a) for(int b=1; b<=m; ++b) for(int i=a; i<=n; ++i) for(int j=b; j<=m; ++j) if((sum[i][j] - sum[a-1][j] - sum[i][b-1] + sum[a-1][b-1]) % k == 0) ++Ans; printf("%d\n",Ans); return 0; }
//AC 程式碼
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 410
typedef long long LL;
int a[MAXN][MAXN],b[MAXN],cnt[1000005];
LL sum[MAXN][MAXN];
inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}
LL Ans = 0;
int main(int argc,char *argv[]){
    freopen("rally.in","r",stdin);
    freopen("rally.out","w",stdout);
    int n,m,Mod;
    read(n),read(m),read(Mod);
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j){
            read(a[i][j]);
            sum[i][j] = (sum[i-1][j] + a[i][j] + sum[i][j-1] - sum[i-1][j-1]);
            if(sum[i][j] >= Mod) sum[i][j] -= Mod;
        }
    for(int i=0; i<n; ++i)
        for(int j=i+1; j<=n; ++j){
            cnt[0] = 1;
            for(int k=1; k<=m; ++k) {
                b[k] = (sum[j][k] - sum[i][k] ) % Mod;
                if(b[k] < 0) b[k] += Mod;
                Ans += cnt[b[k]];
                ++cnt[b[k]];
            }
            for(int k=1; k<=m; ++k) cnt[b[k]] = 0;
        }
    printf("%I64d\n",Ans);
    fclose(stdin); fclose(stdout);
    return 0;
}

建議先看這道題:連結
關於這個AC程式碼,我得好好說說道道,i、j列舉子矩陣的上下邊界,k列舉我們處理到了第幾列,把i、j行之間,右邊界是第k列的矩陣壓成一個數,之後列舉統計,具體原理在連結的那一篇部落格說的很詳細,N^3可過,還給開O2優化。

將軍令(general)

【題目描述】
歷史/落在/贏家/之手
至少/我們/擁有/傳說
誰說/敗者/無法/不朽
拳頭/只能/讓人/低頭
念頭/卻能/讓人/擡頭
擡頭/去看/去愛/去追
你心中的夢
又想起了四月。
如果不是省選,大家大概不會這麼輕易地分道揚鑣吧?
只見一個又一個昔日的隊友離開了機房。
憑君莫話封侯事,一將功成萬骨枯。
夢裡,小 F 成了一個給將軍送密信的信使。
現在,有兩封關乎國家生死的密信需要送到前線大將軍帳下,路途凶險,時間緊迫。小
F 不因為自己的禍福而避趨之,勇敢地承擔了這個任務。
不過,小 F 實在是太粗心了,他一不小心把兩封密信中的一封給弄掉了。
小 F 偷偷打開了剩下的那封密信。 他發現一副十分詳細的地圖, 以及幾句批文——原來
這是戰場周圍的情報地圖。他仔細看後發現,在這張地圖上標記了 ? 個從 1 到 ? 標號的
驛站,? − 1 條長度為 1 裡的小道,每條小道雙向連線兩個不同的驛站,並且驛站之間可以
通過小道兩兩可達。
小 F 仔細辨認著上面的批註,突然明白了丟失的信的內容了。原來,每個驛站都可以駐
扎一個小隊,每個小隊可以控制距離不超過 ? 裡的驛站。如果有驛站沒被控制,就容易產
生危險——因此這種情況應該完全避免。 而那封丟失的密信裡, 就裝著朝廷數學重臣留下的
精妙的排布方案,也就是用了最少的小隊來控制所有驛站。
小 F 知道,如果能計算出最優方案的話,也許他就能夠將功贖過,免於死罪。他找到了
你,你能幫幫他嗎?
當然, 小 F 在等待你的支援的過程中, 也許已經從圖上觀察出了一些可能會比較有用的
性質,他會通過一種特殊的方式告訴你。
【輸入格式】
從檔案 general.in 中讀入資料。
輸入第 1 行一個正整數 ?,?,?,代表驛站數,一支小隊能夠控制的最遠距離,以及特
殊性質所代表的編號。關於特殊性質請參照資料範圍。
輸入第 2 行至第 ? 行,每行兩個正整數 ? ? ,? ? ,表示在 ? ? 和 ? ? 間,有一條長度為
一里的小道。
【輸出格式】
輸出到檔案 general.out 中。
輸出一行,為最優方案下需要的小隊數。
【樣例 1 輸入】
4 1 0
1 2
1 3
1 4
【樣例 1 輸出】
1
【樣例 1 說明】
如圖。由於一號節點到周圍的點距離均是 1,因此可以控制所有驛站。
這裡寫圖片描述
【樣例 2 輸入】
6 1 0
1 2
1 3
1 4
4 5
4 6
【樣例 2 輸出】
2
【樣例 2 說明】
如圖,和樣例 1 類似。
這裡寫圖片描述

【資料範圍】
子任務會給出部分測試資料的特點。如果你在解決題目中遇到了困難,可以嘗試只解
決一部分測試資料。
關於 ? 的含義如下:
0:該測試點沒有額外的特殊性質;
1最多 8 個點的所連線的小道超過 1 條;
? = 2:保證所有點到 1 號點的距離不超過 2。
每個測試點的資料規模及特點如下表:
這裡寫圖片描述

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

#define MAXN 100005
struct Edge{ int to,next; }e[MAXN << 1];
int head[MAXN],fa[MAXN],tot,que[MAXN],dis[MAXN];
bool exist[MAXN];

inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

inline void Add_Edge(int u,int v){
    e[++tot].to = v, e[tot].next = head[u], head[u] = tot;
    e[++tot].to = u, e[tot].next = head[v], head[v] = tot;
}

void BFS(){
    fa[1] = 1, que[1] = 1;
    int top = 1,tail = 2;
    while(top < tail){
        int u = que[top++];
        for(int i=head[u]; i; i=e[i].next){
            int v = e[i].to;
            if(v == fa[u]) continue;
            fa[v] = u, que[tail++] = v;
        }
    }
}

void DFS(int u,int dep){
    exist[u] = true, dis[u] = dep;
    if(!dep) return;
    for(int i=head[u]; i; i=e[i].next){
        int v = e[i].to;
        if(dis[v] < dis[u]-1 || !exist[v]) DFS(v,dep-1);
    }
}

int main(int argc,char *argv[]){
    freopen("general.in","r",stdin); 
    freopen("general.out","w",stdout);
    int n,K,t;
    read(n),read(K),read(t);
    for(int u,v,i=1; i<=n-1; ++i){
        read(u),read(v);
        Add_Edge(u,v);
    }
    BFS();
    memset(dis,0,sizeof dis );
    int Ans = 0;
    for(int i=n; i; --i){
        int x = que[i];
        if(!exist[x]){
            ++Ans;
            for(int j=K; j; --j) x = fa[x];
            dis[x] = K;
            DFS(x,K);
        }
    }
    printf("%d\n",Ans);
    fclose(stdin); fclose(stdout);
    return 0;
}

對於某個沒有被控制起來的點,肯定是在他的k級祖先上設立小隊更優—> 貪心(樹形DP也可以吧)

只用exist陣列是不行的,為什麼要記錄一個dis陣列(記錄裡最近的小隊的距離),如果不記錄有可能會在他的k級祖先設立小隊的時候會把他的兄弟的兒子堵死,導致另外多設一個小隊。。。大概意會一下吧。。

T3

關於T3並不想說什麼