2008年成都网络赛训练总结

     本次训练赛由我、DTen和sssplk完成,三台机子同时进行。

     本次训练我们发挥得并不好,只A了一题,但是第一题是很快的1A.然后我就开始卡题,4个小时半卡在B题-矩阵乘法,而DTen则对剩下的题目不知所措,sssplk负责几何也对那道几何无可奈何。这场比赛我能做的题比较多,但是因为坑爹的杭电只提供十分渺小的系统栈,导致我的矩阵二分时递归RE,,然后找各种模板来取代递归版,咳,都是RE.

     

     赛后我一共A了7道题,一道几何比较简单但没去敲,现在说下这场比赛的题目:

      2428、Stars   水题。因为是正方形,那么枚举两点,如果它们同线,另外两点的坐标也确定了。用hash来存坐标,然后直接判定那两点是否存在就好,秒杀之。

      2429、Word Game  矩阵乘法,给定n个串,当某串的末尾字符等于其他串的首字符时表示他们可以相连,用这些串可以组成一个表示是否能相连的矩阵,也是一个有向图。然后问走不大于n步并且步数为奇数后是目标T串的总方法数。设矩阵是A,那么我们求的就是A^1 + A^3 + ...A^n,再转换变成A1+A1 *( (A^2)^1 + (A^2)^2...(A^2)^k),然后跑矩阵乘法n…n^3log(k)的复杂度解决。杭电改版后系统栈特别小,这题不能用递归式算法,并未那个k特别大。详细解题报告见Here.

      2430、Beans   很多方法都可以过,我用单调队列做。有n坨豌豆,每坨都有w[i]个,现在要从中选择连续的若干坨,然后用一个能装p个豌豆的背包装豆子,直到剩下的豌豆数量<=p为止,还要求剩下的豌豆数量少于k,问最多能装多少包。那么选择连续的一段和为sum,使得max(sum/p) 且sum%p<=k,这个sum可以用sum[i] - sum[j]来表示,那么确定了i,就是找最小的j使得(sum[i] - sum[j]) % p <= k,这时和最大。将每个和对p取余得到一个余数r数组,然后按余数大小优先位置其次的顺序排序。排完序之后就可以根据ri - k <= rj <= ri来往前找那个最小的j。遍历数组r,因为r是有序的,我用单调队列储存r小于当前的最小下标。详细解题报告见Here

      2431、Counting Problem  很难转换模型,但是转换后就是水题,训练没做出来,智商是硬伤啊。每次固定左上方边长为n-2,n-3...n-k的子正方形(n-k >= k),剩下边长为2,3....k的子正方形随意放,设答案是dp[n],dp[n] = dp[n-2] + dp[n-3] +..dp[n-k] (n-k<=k).这式子看起来不好写,但实际上用两个for循环就可以,第一位表示这个k,然后每次都加上dp[j-i]。具体见代码。


     2432、Farey Sequence Again  数论。第一次听到这种法雷级数,算见了市面。因为k<n这个条件的约束使得本题特别简单,因为小于n个的话分子要么是1,要么是2,要么是3,为什么,打表打几个看下然后YY即可得。得到上面的yy结论后,用O(1)的复杂度判断到底第k个法雷级数分子是1还是2还是3,n个可以分成1/2,1/3,以及剩下来的,1/2并不是严格的,而是(1+n) / 2,1/3是(n+2)/3,也就是上取整。第1部分分子是1,第2部分分子是2,第3部分比较难想,有1有2有3,那么将分子搞成6的话分母又是连续的,每6个连续的分母里面有2个是和6互质,那么就有4个分母可以进行约分。这样就找第几个4以及判断是4个中的第几个即可。

     2433、Travel  很好的搜索题。给定n个点m条边的无向连通图,Sum表示每个点到其他所有点的最小距离和,问我们删去某条边之后的Sum。朴素做法是枚举每条边,然后进行n次BFS,复杂度是O(n*m^2)。这个复杂度太高了,我们为了降低复杂度把枚举边转换成和点相关的操作,那么复杂度就有可能是O(n^2*m),这样就应该能过。我们先预处理,做n次BFS,把每个点到其他所有点的最短距离求出来,并求出都经过哪些边,叫它关键边,如果某条边是这点的关键边,那么就要更新一次,再做一次BFS,否则直接用之前预处理求出的Sum即可。本题很值得学习的是这个预处理,我觉得和炮兵阵地的预处理一样漂亮,极大地降低了复杂度。

     2437、Jerboas 拓朴排序+DP,好题。给定一个n个点m条边的有向无环图,每个点表示一个洞,洞有临时和永久之分,从临时洞开始找一个永久洞结束,要求距离为k的倍数并要求距离最小。也就是说我们要找一个点,他的距离对k取模余数为0并且除数最小,这样到每个点最多1000种状态,我们设个Dp[i][j],表示到i点状态j时对k的最小除数dp[i][j]*k+j就是最小距离。这样状态转移方程就是: Dp[next][j+(j+len)%k] = min(Dp[next][j+(j+len)%k],Dp[i][j]+(j+len)/k)(i有边连向next,边权为len),复杂度是O(n*m)

    

     本次训练状态确实不好,很多题目并不难,看到那些约束条件就可以轻松地转换模型,然后就可以做了。卡题卡得死死的,悲剧了...下不为例,一题最多卡1个半小时。

     这次比赛还是学到挺多东西,比如法雷级数,比如当复杂度过高时考虑预处理,比如枚举某个变量复杂度过高时考虑枚举另一个变量,还有J题我也很喜欢,喜欢那种表示状态的方式。

     最后给源代码。后面的三题过几天再做,网络流没学,模拟题又巨恶心。

     

     A :

#include <stdio.h>
#include <string.h>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define MAX 1200


struct node {

    int x,y;
}arr[MAX];
int n,m,ans,cnt;
bool vis[MAX*2][MAX*2];
map<int,int> hash;


int cmp(node a,node b) {

    if (a.x == b.x) return a.y > b.y;
    else return a.x > b.x;
}


int main()
{
    int i,j,k,t;


    scanf("%d",&t);
    while (t--) {

        scanf("%d",&n);
        ans = cnt = 0;
        hash.clear();
        memset(vis,false,sizeof(vis));
        for (i = 1; i <= n; ++i) {

            scanf("%d%d",&arr[i].x,&arr[i].y);
            //hash[arr[i]] = ++cnt;
            if (hash.find(arr[i].x) == hash.end())
                hash[arr[i].x] = ++cnt;
            if (hash.find(arr[i].y) == hash.end())
                hash[arr[i].y] = ++cnt;
            vis[hash[arr[i].x]][hash[arr[i].y]] = true;
        }


        sort(arr+1,arr+1+n,cmp);
        for (i = 1; i <= n; ++i)
            for (j = i + 1; j <= n; ++j)
                if (arr[i].x == arr[j].x){

                int len = arr[i].y - arr[j].y;
                len = abs(len);
                node cur;
                cur.x = arr[i].x + len;
                cur.y = arr[i].y;
                if (hash.find(cur.x) != hash.end()
                        && hash.find(cur.y) != hash.end()) {
                    if (vis[hash[cur.x]][hash[cur.y]] == 0)
                        continue;
                    cur.x = arr[j].x + len;
                    cur.y = arr[j].y;
                     if (hash.find(cur.x) != hash.end()
                        && hash.find(cur.y) != hash.end())
                         if (vis[hash[cur.x]][hash[cur.y]] == 1)
                             ans++;
                }
            }

        
        printf("%d\n",ans);
    }
}

     B:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#define MAX 100
#define MOD 10001
#define int64 int//long long//__int64


int n, begin, end;
int64 m,final[MAX][MAX];
char str[MAX][MAX],tp[MAX];
struct Mat {

    int mat[MAX][MAX], size;

    Mat() {size = MAX;};
    Mat(int n) {

        size = n;
        memset(mat, 0, sizeof (mat));
    }


    friend Mat operator *(Mat a, Mat b);
    friend Mat operator +(Mat a, Mat b);
    friend Mat operator ^(Mat a, int64 k);
} E;
Mat operator *(Mat a, Mat b) {

    Mat c;
    c.size = a.size;
    memset(c.mat, 0, sizeof (c.mat));
    for (int i = 0; i < c.size; ++i)
        for (int j = 0; j < c.size; ++j)
            for (int k = 0; k < c.size; ++k)
                if (a.mat[i][k] && b.mat[k][j])
                    c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD;
    return c;
}
Mat operator +(Mat a, Mat b) {

    Mat c;
    c.size = 2 * n;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            c.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % MOD;
    return c;
}
Mat operator ^(Mat a, int64 k) {

    Mat c = E;
    c.size = a.size;
    while (k) {

        if (k & 1) c = c * a;
        a = a * a, k >>= 1;
    }
    return c;
}
Mat sum1(Mat A,int k) {
	//空间复杂度为O(n*n),时间复杂度为O(log(k)*n^3)
	if (k == 1) return A;
	if (k & 1)  return sum1(A,k-1) + (A^k);
	return sum1(A,k/2) * ((A^(k / 2)) + E);
}
Mat sum2(Mat A, int64 k) {

    int i,j;
    Mat c = A;
    c.size = 2 * A.size;
    for (i = 0; i < n; ++i)
       c.mat[i][i+n] = c.mat[i+n][i+n] = 1;

    
    c = c^(k+1);
    Mat temp;
    temp.size = n;
    for (i = 0; i < n; ++i)
        for (j = 0; j < n; ++j)
            temp.mat[i][j] = (i==j?c.mat[i][j+n]-1:c.mat[i][j+n]);
    return temp;
}
int Solve() {

    int i, j;
    Mat c;
    E.size = c.size = n;
    memset(c.mat, 0, sizeof (c.mat));
    memset(E.mat, 0, sizeof (E.mat));
    for (i = 0; i < 2 * n; ++i) E.mat[i][i] = 1;
    for (i = 0; i < n; ++i)
        for (j = 0; j < n; ++j)
            if (str[i][strlen(str[i]) - 1] == str[j][0])
                c.mat[i][j] = 1;


    if (m % 2 == 0) m--;
    int cnt = (m - 1) / 2;
    Mat c2 = c * c;
    int ans = 0;
    c = c + c * sum2(c2,cnt);
    ans = (ans + c.mat[begin][end]) % MOD;
    return ans;
}


int main()
{
    int i, j, k,t;


    scanf("%d", &t);
    while (t--) {

        scanf("%d", &n);
        for (i = 0; i < n; ++i)
            scanf("%s",str[i]);
        scanf("%s",tp);
        for (i = 0; i < n; ++i)
            if (strcmp(tp,str[i]) == 0)
                begin = i;
        scanf("%s",tp);
        for (i = 0; i < n; ++i)
            if (strcmp(tp,str[i]) == 0)
                end = i;
        scanf("%d",&m);


        int ans = Solve();
        printf("%d\n",ans%MOD);
    }
}

    C:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX 1000010
#define max(a,b) (a)>(b)?(a):(b)


struct node {
    
    int x,in;
}r[MAX];
int sum[MAX],mmin[MAX];
int n,p,k,ans,hash[MAX];


int cmp(node a,node b) {
    
    if (a.x == b.x) return a.in > b.in;
    else return a.x < b.x;
}


int main()
{
    int i,j,t;
    int cas = 0,mmin;
    
    
    scanf("%d",&t);
    while (t--) {
        
        scanf("%d%d%d",&n,&p,&k);
        for (i = 1; i <= n; ++i) {
            
            scanf("%d",&sum[i]);
            r[i].in = i;
            sum[i] += sum[i-1];
            r[i].x = sum[i] % p + k;
        }
        
        
        sort(r+1,r+1+n,cmp);
        //for (i = 1; i <= n; ++i)
         //   printf("%d in = %d\n",r[i].x,r[i].in);
        mmin = n + 1;
        for (i = n; i >= 1; --i) {
            
            hash[r[i].in] = i;
            int temp = r[i].in;
            r[i].in = mmin;
            if (temp < mmin)
                mmin = temp;
        }


        //for (i = 1; i <= n; ++i)
            //printf("i = %d r[i] = %d\n",i,r[i].in);
        ans = -1;
        for (i = 1; i <= n; ++i) {

            int index = r[hash[i]].in;
            if (sum[i] % p <= k) ans = max(ans,sum[i] / p);
            if (r[index].in < i) ans = max(ans,(sum[i] - sum[r[index].in])/ p);
            //printf("i = %d ans = %d\n",i,ans);
        }


        printf("Case %d: %d\n",++cas,ans);
    }
}

    

    D:

#include <stdio.h>
#include <string.h>
#define MAX 510
#define MOD 1000007


int main()
{
    int i,j,k,n,t;
    int dp[MAX] = {0};
    dp[0] = 1;
    for (i = 2; i < MAX; ++i)
        for (j = i; j < MAX; ++j)
            dp[j] = (dp[j] + dp[j-i]) % MOD;
    
    
    scanf("%d",&t);
    while (t--) {
        
        scanf("%d",&n);
        printf("%d\n",dp[n]);
    }
}

   E:

#include <stdio.h>
#include <string.h>


int Gcd(int x,int y) {

    int r = x % y;
    while (r) {

        x = y,y = r;
        r = x % y;
    }
    return y;
}


int main()
{
    int i,j,k,t;
    int n,rest,bigest;
    

    scanf("%d",&t);
    while (t--) {
        
        scanf("%d%d",&n,&k);
        rest = (n + 1) / 2;//上取整
        if (k <= rest) {
        //前半部分,分子为1,分母递减
            printf("1/%d\n",n-k+1);
            continue;
        }


        k = k - rest;
        rest = (n + 2) / 3;
        if (k <= rest) {
        //中间部分,分子为2,分母递减
            j = Gcd(2,n-k+1);
            printf("%d/%d\n",2/j,(n-k+1)/j);
            continue;
        }


        k = k - rest;
        rest = (k - 1) % 4 + 1;
        bigest = n * 2 - (k - 1) / 4 * 6;
        while (1) {
            
            j = Gcd(6,bigest);
            if (j == 1) {
            //最大公约数为1,不是法雷级数
                bigest--;
                continue;
            }
            else if (rest == 1) {
            //最后一个法雷级数
                printf("%d/%d\n",6/j,bigest/j);
                break;
            }
            bigest--,rest--;
        }
    }
}

     F:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
#define MIN 110
#define MAX 3300


struct bnode {
    //广搜用的node
    int dis,v;
}qu[MAX];
struct mnode {
    //建图用的node
    int v,in;
    mnode *next;
}map[MAX*2],*Head[MIN];
bool vis[MIN][MAX],used[MIN][MAX];//广搜用的判点是否搜过,判某点是否在最小路径树上
int  x[MAX],y[MAX],ans,flag;
int  ptr,n,m,sum[MAX],head,tail;


void Initial() {
    
    ptr = 0;
    memset(vis,0,sizeof(vis));
    memset(used,0,sizeof(used));
    memset(Head,NULL,sizeof(Head));
}
void AddEdge(int x,int y,int z) {
    
    map[ptr].v = y,map[ptr].in = z;
    map[ptr].next = Head[x],Head[x] = &map[ptr++];
}
int Bfs_AC(int cur,int index) {

    int cnt = 0;
    bnode now,next;


    sum[index] = 0;
    head = tail = 0;
    now.v = cur,now.dis = 0;
    qu[++head] = now;
    vis[index][cur] = true;
    
    
    while (tail < head) {

        cnt++;
        now = qu[++tail];
        sum[index] += now.dis;
        mnode *p = Head[now.v];
        while (p != NULL) {
            
            int v = p->v,in = p->in;
            if (!vis[index][v]
                    && !used[index][in]) {

                next.v = v;
                vis[index][v] = true;
                used[index][in] = true;
                next.dis = now.dis + 1;
                qu[++head] = next;
            }
            p = p->next;
        }
    }
    return cnt;
}


int main()
{
    int i,j,k,t,ta,tb;
    
    
    while (scanf("%d%d",&n,&m) != EOF) {
        
        Initial();
        for (i = 1; i <= m; ++i) {
            
            scanf("%d%d",&x[i],&y[i]);
            AddEdge(x[i],y[i],i),AddEdge(y[i],x[i],i);
        }
        
        
        for (i = 1; i <= n; ++i) Bfs_AC(i,i);
        //Debug_Input
        for (j = 1; j <= m; ++j) {

            ans = flag = 0;
            for (i = 1; i <= n && !flag; ++i) {

                if (!used[i][j]) ans += sum[i];
                else {

                    memset(vis[n+1],false,sizeof(vis[n+1]));
                    memset(used[n+1],false,sizeof(used[n+1]));
                    used[n+1][j] = true;
                    ta = Bfs_AC(i,n+1);
                    if (ta < n) flag = 1;
                    else ans += sum[n+1];
                }
            }


            if (flag) printf("INF\n");
            else printf("%d\n",ans);
        }
    }
}

     J:

#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;
#define MAX 1001
#define INF 2147483647
#define min(a,b) ((a)<(b)?(a):(b))


struct node {
    
    int v,len;
}cur,next,qu[MAX];
vector<node> map[MAX];
char str[MAX];
int top,st[MAX],cnt;
int count[MAX],topo[MAX];
int s,k,n,m,ans,ansi,dp[MAX][MAX];


void Initial() {
    
    ansi = ans = -1;
    for (int i = 0; i <= n; ++i) {

        map[i].clear();
        count[i] = 0;
        for (int j = 0; j <= k; ++j)
            dp[i][j] = INF;
    }
}
void ToooPooo() {

    int i,j,v,size;


    cnt = top = 0;
    for (i = 1; i <= n; ++i)
        if (count[i] == 0) st[++top] = i;


    while (top > 0) {

        i = st[top--];
        topo[++cnt] = i;
        size = map[i].size();
        for (j = 0; j < size; ++j) {

            cur = map[i][j];
            count[cur.v]--;
            if (count[cur.v] == 0)
                st[++top] = cur.v;
        }
    }
}
void Debug_Input() {

    int i,j,t;
    for (i = 1; i <= n; ++i) 
        for (j = 0; j < k; ++j)
            printf("i = %d j = %d dp[i][j] = %d\n",i,j,dp[i][j]);
}
int Solve_AC() {
    
    int i,j,t,size,v,tp;


    dp[s][0] = 0;
    for (i = 1; i <= cnt; ++i) {

        v = topo[i];
        size = map[v].size(); 
        for (j = 0; j < size; ++j) {

            next = map[v][j];
            for (t = 0; t < k; ++t)
                if (dp[v][t] != INF) {

                    tp = (t + next.len) % k;
                    dp[next.v][tp] = min(dp[next.v][tp],dp[v][t] + (t+next.len)/k);
                }
        }
    }


    for (i = 1; i <= n; ++i)
        if (str[i] == 'P' && dp[i][0] != INF
                && (ans == -1 || dp[i][0] * k < ans))
            ansi = i,ans = dp[i][0] * k;
    return ansi;
}


int main()
{
    int i,j,t;
    int cas = 0,a,b,c;
    

    scanf("%d",&t);
    while (t--) {

        scanf("%d%d%d%d",&n,&m,&s,&k);
        scanf("%s",str+1);
        Initial();


        for (i = 1; i <= m; ++i) {
            
            scanf("%d%d%d",&a,&b,&c);
            count[b]++;
            cur.v = b,cur.len = c;
            map[a].push_back(cur);
        }
        
        
        ToooPooo();
        Solve_AC();
        printf("Case %d: %d %d\n",++cas,ans,ansi);
    }
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(2008年成都网络赛训练总结)