Bestcoder "BestCoder"杯总结

2015-05-07 11:32:38

总结:hdu 主办的 “bc” 杯的网络赛~

  12.30开始,历时5h...

  比赛中搞掉了5题... room rank3 抢到一件 t 恤 0.0...

  赛后补了下题... 最终8题... 本来想补一下 07,但进度不允许啦QAQ。

 

1001题 hdu 5214:贪心算法

  题意就是根据一定规律生成 N 个区间,问能否找出三个两两相离的区间。 Li = (Li1  a + b) mod 4294967296, Ri = (Ri1  c + d) mod 4294967296

  观察生成规律发现,模数就是unsigned int的最大值,所以就直接用unsigned int来保存各个变量使之自然溢出即可(取模会 TLE)

  接着考虑贪心法,只要找到一个最左区间和一个最右区间,再检查是否有一个区间在其中即可。而最左区间我们只考虑右端点最小值,最右区间只考虑左端点最大值。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const ll mod = 4294967296LL;

int T;
unsigned int N,L,R,a,b,c,d;

unsigned int Max(unsigned int a,unsigned int b){ return a > b ? a : b; }
unsigned int Min(unsigned int a, unsigned int b){ return a < b ? a : b; }

int main(){
    scanf("%d",&T);
    while(T--){
        int tn,tl,tr,ta,tb,tc,td;
        scanf("%d%d%d%d%d%d%d",&tn,&tl,&tr,&ta,&tb,&tc,&td);
        N = tn; L = tl; R = tr; a = ta; b = tb; c = tc; d = td;
        unsigned int tL = L,tR = R;
        unsigned int tmin = Max(L,R);
        unsigned int tmax = Min(L,R);
        for(int i = 2; i <= N; ++i){
            tL = tL * a + b;
            tR = tR * c + d;
            tmin = Min(tmin,Max(tL,tR));
            tmax = Max(tmax,Min(tL,tR));
        }
        bool flag = false;
        tL = L;
        tR = R;
        if(Min(tL,tR) > tmin && Max(tL,tR) < tmax){
             printf("YES\n");
             continue;
        }
        for(int i = 2; i <= N; ++i){
            tL = tL * a + b;
            tR = tR * c + d;
            if(Min(tL,tR) > tmin && Max(tL,tR) < tmax){
                flag = true;
                break;
            }
        }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
View Code

 

1002题 hdu 5215:边双连通分量,tarjan,桥,二分图染色

  题意很简单,给出一个无向图,问是否存在奇环和偶环。

  比赛的时候打了一发 dfs树+lca 竟然莫名其妙地过了,后来发现这是不对的,这样会导致多算(因为我把子树中的奇环都看做相交的了),比如两个相离的奇环就能 hack 掉。

  正解比较多样,这里写的是题解的做法。

  1. 首先通过二分图染色来找奇环。根据定理:二分图必定不存在奇环,非二分图必定存在奇环。

  2. 其次用 tarjan 找出所有桥,再进行 dfs(不经过桥) 来分离出边双连通分量。

  3. 对于每个边双连通分量,如果其点数 = 边数,那么必定是个简单环,直接判断环长度的奇偶性即可;

    如果边数 > 点数,那么是复杂环,可以证明复杂环内必定有偶环。

    (复杂环至少由两个环组成,若有小偶环则存在偶环;若只有小奇环,根据小奇环数>=2,必有两个小奇环相交,两奇环的长度 - 公共边长度×2 = 偶数,则存在大偶环。)

  后记:这题用带权并查集来做会比较简便。

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 100010;

int T,N,M;
int first[MAXN],ecnt; //edge
int dfn[MAXN],bg[MAXN],low[MAXN],bcc[MAXN],bcnt,tot; //tarjan , bcc
int ver[MAXN],edge[MAXN],co[MAXN]; //bcc , bipartite graph
bool odd,even;

struct Edge{
    int u,v,next;
}e[MAXN * 6];

void Init(){
    memset(first,-1,sizeof(first));
    ecnt = bcnt = tot = 0;
}

void add_edge(int u,int v){
    e[ecnt].next = first[u];
    e[ecnt].u = u;
    e[ecnt].v = v;
    first[u] = ecnt++;
}

void Dfs(int p,int pre){
    dfn[p] = low[p] = ++tot;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(v == pre) continue;
        if(!dfn[v]){
            Dfs(v,p);
            low[p] = min(low[p],low[v]);
            if(low[v] > dfn[p]) bg[i] = bg[i ^ 1] = 1;
        }
        else low[p] = min(low[p],dfn[v]);
    }
}

void Tarjan(){
    memset(bg,0,sizeof(bg));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    for(int i = 1; i <= N; ++i)
        if(!dfn[i]) Dfs(i,0);
}

void Dfs2(int p,int pre){
    ver[bcnt]++;
    bcc[p] = bcnt;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(bcc[v] || bg[i]) continue;
        Dfs2(v,p);
    }
}

void BCC(){
    memset(edge,0,sizeof(edge));
    memset(ver,0,sizeof(ver));
    memset(bcc,0,sizeof(bcc));
    for(int i = 1; i <= N; ++i) if(!bcc[i]){
        ++bcnt;
        Dfs2(i,0);
    }
    for(int i = 0; i < ecnt; i += 2)
        if(bcc[e[i].u] == bcc[e[i].v]) edge[bcc[e[i].u]]++;
}

int Dfs_co(int p,int col){
    if(co[p] == -1) co[p] = col;
    else return co[p];
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(co[p] == Dfs_co(v,col ^ 1)){
            odd = true;
            break;
        }
    }
    return co[p];
}

void Draw(){
    memset(co,-1,sizeof(co));
    for(int i = 1; i <= N; ++i) if(co[i] == -1){
        Dfs_co(i,0);
        if(odd) break;
    }
}

int main(){
    int a,b;
    scanf("%d",&T);
    while(T--){
        Init();
        scanf("%d%d",&N,&M);
        for(int i = 1; i <= M; ++i){
            scanf("%d%d",&a,&b);
            add_edge(a,b);
            add_edge(b,a);
        }
        Tarjan();
        BCC();
        odd = even = false;
        Draw();
        for(int i = 1; i <= bcnt; ++i){
            if(ver[i] == edge[i] && edge[i] % 2 == 0) even = true;
            if(ver[i] < edge[i]) even = true;
        }
        if(odd) printf("YES\n");
        else printf("NO\n");
        if(even) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
View Code

 

1003题 hdu 5216:技巧题

  比较简单的一题,找到规律就行,题意不赘述了。

  仔细思考就会发现,将两个数列排序,然后对应位置形成一个个区间就行。因为交换两个数后被染黑的区间仍然一样,所以交换并没有意义。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 1000010;

int N,M,T;
int A[MAXN],B[MAXN];
int vis[MAXN];

int main(){
    scanf("%d",&T);
    while(T--){
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&N,&M);
        for(int i = 1; i <= M; ++i) scanf("%d",&A[i]);
        for(int i = 1; i <= M; ++i) scanf("%d",&B[i]);
        sort(A + 1,A + M + 1);
        sort(B + 1,B + M + 1);
        bool flag = true;
        for(int i = 1; i <= M; ++i){
            if(A[i] > B[i]){
                flag = false;
                break;
            }
            for(int j = A[i]; j <= B[i]; ++j) vis[j] = 1;
        }
        if(flag == false) printf("Stupid BrotherK!\n");
        else{
            int cur = 0,tmax = 0;
            for(int i = 1; i <= N; ++i){
                if(!vis[i])
                    cur++;
                else{
                    tmax = max(tmax,cur);
                    cur = 0;
                }
            }
            tmax = max(tmax,cur);
            printf("%.6f\n",(double)tmax);
        }
    }
    return 0;
}
View Code

 

1004题 hdu 5217:线段树*,技巧,分段处理

  一道线段树中难题,比赛中过的人不多...

  题意:给出N个括号,每次询问给出一个区间[l,r]以及k,问将区间内左右匹配的括号约掉之后,第k个括号在什么位置。

  思路:用一颗线段树维护每个区间约掉匹配的括号后还剩几个 '(' 和几个 ')',我们可以发现约掉之后必然是:" )))..(((" 这种形式,那么

  区间就可以合并了(通过简单计算可以知道两个区间合并后的 '(' 和 ')' 个数)。

  ※对于修改,很容易进行。

  ※对于每个询问:我们可以按照线段树将这个区间分成若干块(logn级别... 这个过程有点像分块,不过分块是sqrtn),然后我们需要

    发现两个单调性:从左到右合并区间 ')' 的个数不会减少,从右到左合并区间 '(' 的个数不会减少。那么我们只要先预处理一下区间里面

    的所有块,统计出 '(' 和 ')' 的个数,如果左右括号的总数 < k,那么答案为-1,不然看 ')' 的个数是否大于 k,如果是那么第k个为 ')',否则第k个为 '('。

    (1)如果第k个为 ')' 右括号,那么从左到右扫一遍所有块,确定第k个右括号在哪一块。

      然后开始 dfs ,利用线段树计算一下答案位于左半部分还是右半部分,深入下去,最终找到答案,logn的深度。

    (2)如果第k个为 '(' 左括号,类似。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 200010;
const int MAX_LOG = 100;

int T,N,Q;
char s[MAXN];
int tL[MAXN << 2],tR[MAXN << 2];
int block[MAX_LOG][3],bcnt;

inline void Push_up(int p){
    tR[p] = tR[p << 1] + max(0,tR[p << 1|1] - tL[p << 1]);
    tL[p] = tL[p << 1|1] + max(0,tL[p << 1] - tR[p << 1|1]);
}

inline void Build_tree(int p,int l,int r){
    if(l == r){
        tL[p] = s[l] == '(' ? 1 : 0;
        tR[p] = s[l] == ')' ? 1 : 0;
        return;
    }
    int mid = getmid(l,r);
    Build_tree(p << 1,l,mid);
    Build_tree(p << 1|1,mid + 1,r);
    Push_up(p);
}

inline void Update_tree(int a,int p,int l,int r){
    if(l == r){
        tL[p] ^= 1;
        tR[p] ^= 1;
        return;
    }
    int mid = getmid(l,r);
    if(a <= mid) Update_tree(a,p << 1,l,mid);
    else Update_tree(a,p << 1|1,mid + 1,r);
    Push_up(p);
}

void Block(int a,int b,int p,int l,int r){
    if(a <= l && r <= b){
        block[++bcnt][0] = p;
        block[bcnt][1] = l;
        block[bcnt][2] = r;
        return;
    }
    int mid = getmid(l,r);
    if(a <= mid) Block(a,b,p << 1,l,mid);
    if(b > mid) Block(a,b,p << 1|1,mid + 1,r);
}

inline int Query_pre_tree(int curL,int curR,int k,int p,int l,int r){
    if(l == r) return l;
    int mid = getmid(l,r);
    int nxtR = curR + max(0,tR[p << 1] - curL);
    int nxtL = tL[p << 1] + max(0,curL - tR[p << 1]);
    if(nxtR >= k) return Query_pre_tree(curL,curR,k,p << 1,l,mid);
    else return Query_pre_tree(nxtL,nxtR,k,p << 1|1,mid + 1,r);
}

inline int Query_suf_tree(int curL,int curR,int k,int p,int l,int r){
    if(l == r) return l;
    int mid = getmid(l,r);
    int nxtR = tR[p << 1|1] + max(0,curR - tL[p << 1|1]);
    int nxtL = curL + max(0,tL[p << 1|1] - curR);
    if(nxtL >= k) return Query_suf_tree(curL,curR,k,p << 1|1,mid + 1,r);
    else return Query_suf_tree(nxtL,nxtR,k,p << 1,l,mid);
}

int main(){
    int op,l,r,k;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&Q);
        scanf("%s",s + 1);
        Build_tree(1,1,N);
        for(int q = 1; q <= Q; ++q){
            scanf("%d",&op);
            if(op == 1){
                scanf("%d",&k);
                Update_tree(k,1,1,N);
            }
            else{
                scanf("%d%d%d",&l,&r,&k);
                bcnt = 0;
                Block(l,r,1,1,N);
                //printf("res : %d %d\n",res.first,res.second);
                int ans,p,curL = 0,curR = 0,preL,preR;
                for(int i = 1; i <= bcnt; ++i){
                    p = block[i][0];
                    preR = curR,preL = curL;
                    curR += max(0,tR[p] - preL);
                    curL = tL[p] + max(0,preL - tR[p]);
                    if(curR >= k){
                        ans = Query_pre_tree(preL,preR,k,p,block[i][1],block[i][2]);
                        break;
                    }
                }
                if(curR >= k){
                    printf("%d\n",ans);
                    continue;
                }
                if(curL + curR < k){
                    printf("-1\n");
                    continue;
                }
                k = curL + curR + 1 - k;
                curL = curR = 0;
                for(int i = bcnt; i >= 1; --i){
                    p = block[i][0];
                    preR = curR,preL = curL;
                    curR = tR[p] + max(0,preR - tL[p]);
                    curL += max(0,tL[p] - preR);
                    if(curL >= k){
                        ans = Query_suf_tree(preL,preR,k,p,block[i][1],block[i][2]);
                        break;
                    }
                }
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}
View Code

 

1005题 hdu 5218:约瑟夫环,dp

  题意:N个人按编号顺时针站成一圈,给出一列M个数A[1~M],总共N-1轮游戏,初始位置为pos=1,每轮游戏从M个数中任意挑选一个A[i],并将位置挪到

  pos+A[i](顺时针移动,N与1相邻),该位置的人出局,如此循环N-1轮,最后剩下的人胜利。问有多少个人可能作为最后的胜利者。

  思路:经典的约瑟夫环问题,QAQ公式记错了... 比赛中又懒得推了... 最后百度了约瑟夫环的公式。

  有逆推公式: f[i] = (f[i - 1] + m) % i (i > 1) , f[i] 表示 i 个人做游戏,最后留下的人的编号。

      ※这题有了一点变化,所以用 dp[i][j] 表示剩 i 个人编号为 j 的人胜出的可能性,可能的话为1,不可能为0。

    初始化:dp[1][0] = 1(假设编号0~N-1)

    dp[i][j]=1 ----转移给---> dp[i+1][(j+A[k])%(i+1)]=1,只要枚举一下k即可。

  复杂度O(N^3)

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 210;

int N,M,T;
int A[MAXN];
int dp[MAXN][MAXN];

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&M);
        for(int i = 1; i <= M; ++i) scanf("%d",&A[i]);
        memset(dp,0,sizeof(dp));
        dp[1][0] = 1;
        for(int i = 1; i < N; ++i){
            for(int j = 0; j < N; ++j){
                if(dp[i][j] == 0) continue;
                for(int k = 1; k <= M; ++k){
                    dp[i + 1][(j + A[k]) % (i + 1)] = 1;
                }
            }
        }
        int cnt = 0;
        for(int j = 0; j < N; ++j) cnt += dp[N][j];
        printf("%d\n",cnt);
        int cur = 0;
        for(int j = 0; j < N; ++j) if(dp[N][j]){
            ++cur;
            printf("%d",j + 1);
            if(cur == cnt) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}
View Code

 

1008题 hdu 5219:树链剖分,线段树,区间更新/查询

  比较明显的树链剖分题。

  首先根据点权建树,保存区间点权和 tsum[],以及区间有效点权和 t[](只计算属于 Miceren 的点)。

  ※对于操作一,区间赋值分体,给每个节点保存一个变量 co,表示区间里的所有点是否都都属于 Miceren 。

  ※对于操作二,单点更新 co 值

  ※对于操作三,仍然是区间赋值,由于一个点的子树在线段树里的编号是连续了,所以更新 p 点的子树,其对应的区间是 [p,p + size[p] - 1],size[p] 表示以 p 为根的子树的大小。

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 100010;

int T,N,Q;
int val[MAXN];
int first[MAXN],ecnt;
int dep[MAXN],sz[MAXN],son[MAXN],fa[MAXN],top[MAXN],w[MAXN],aw[MAXN],tsz;
int t[MAXN << 2],co[MAXN << 2],tsum[MAXN << 2];

struct edge{
    int v,next;
}e[MAXN << 1];

void add_edge(int u,int v){
    e[ecnt].next = first[u];
    e[ecnt].v = v;
    first[u] = ecnt++;
}

void Init(){
    ecnt = tsz = 0;
    memset(first,-1,sizeof(first));
    memset(co,0,sizeof(co));
    memset(t,0,sizeof(t));
}

void Dfs(int p,int pre,int d){
    sz[p] = 1;
    son[p] = -1;
    fa[p] = pre;
    dep[p] = d;
    int v,tmp = 0;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(v == pre) continue;
        Dfs(v,p,d + 1);
        if(sz[v] > tmp){
            tmp = sz[v];
            son[p] = v;
        }
        sz[p] += sz[v];
    }
}

void Dfs_pos(int p,int tp){
    w[p] = ++tsz;
    aw[tsz] = p;
    top[p] = tp;
    if(son[p] != -1) Dfs_pos(son[p],tp);
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(v != son[p] && v != fa[p])
            Dfs_pos(v,v);
    }
}

void Push_down(int a){
    if(co[a]){
        co[a << 1] = co[a << 1|1] = 1;
        t[a << 1] = tsum[a << 1];
        t[a << 1|1] = tsum[a << 1|1];
        co[a] = 0;
        t[a] = 0;
    }
}

void Update_tree(int a,int b,int c,int p,int l,int r){
    if(a <= l && r <= b){
        co[p] = c;
        t[p] = co[p] == 1 ? tsum[p] : 0;
        return;
    }
    Push_down(p);
    int mid = getmid(l,r);
    if(a <= mid) Update_tree(a,b,c,p << 1,l,mid);
    if(b > mid) Update_tree(a,b,c,p << 1|1,mid + 1,r);
    t[p] = t[p << 1] + t[p << 1|1];
    co[p] = co[p << 1] & co[p << 1|1];
}

void Change_1(int a,int b){
    int f1 = top[a],f2 = top[b];
    while(f1 != f2){
        if(dep[f1] > dep[f2]){
            swap(a,b);
            swap(f1,f2);
        }
        Update_tree(w[f2],w[b],1,1,1,tsz);
        b = fa[f2];
        f2 = top[b];
    }
    if(dep[a] > dep[b]) swap(a,b);
    Update_tree(w[a],w[b],1,1,1,tsz);
}

void Change_2(int a){
    Update_tree(w[a],w[a],0,1,1,tsz);
}

void Change_3(int a){
    Update_tree(w[a],w[a] + sz[a] - 1,1,1,1,tsz);
}

void Build_tree(int p,int l,int r){
    if(l == r){
        tsum[p] = val[aw[l]];
        return;
    }
    int mid = getmid(l,r);
    Build_tree(p << 1,l,mid);
    Build_tree(p << 1|1,mid + 1,r);
    tsum[p] = tsum[p << 1] + tsum[p << 1|1];
}

int main(){
    int a,b,c;
    scanf("%d",&T);
    while(T--){
        Init();
        scanf("%d",&N);
        for(int i = 1; i <= N; ++i) scanf("%d",&val[i]);
        for(int i = 1; i < N; ++i){
            scanf("%d%d",&a,&b);
            add_edge(a,b);
            add_edge(b,a);
        }
        Dfs(1,0,0);
        Dfs_pos(1,1);
        Build_tree(1,1,tsz);
        scanf("%d",&Q);
        for(int i = 1; i <= Q; ++i){
            scanf("%d%d",&a,&b);
            if(a == 1){
                scanf("%d",&c);
                Change_1(b,c);
            }
            else if(a == 2) Change_2(b);
            else Change_3(b);
            printf("%d\n",t[1]);
        }
    }
    return 0;
}
View Code

 

1009题 hdu 5222:混合图找环

  给出混合图,判断其中是否有环,每条边只能经过一遍(包括无向边,经过某个方向后就不能从反向再走一次)

  方法比较多:(1)改进版的 Dfs,每个点有三种状态,0表示没有遍历过,-1已经遍历但其后继节点未遍历完,1表示该点以及其后继节点都遍历完毕。

        (2)拓扑排序

        (3)tarjan

  这里用 Dfs 实现。

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 1000010;

int T,N,M1,M2,ecnt;
int first[MAXN];
int used[MAXN];
int ans,vis[MAXN << 2];

struct edge{
    int v,next,id;
}e[MAXN << 2];

void add_edge(int u,int v,int id){
    e[ecnt].id = id;
    e[ecnt].next = first[u];
    e[ecnt].v = v;
    first[u] = ecnt++;
}

void Dfs(int p){
    if(ans) return;
    used[p] = -1;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(vis[i]) continue;
        if(e[i].id == 1)
            vis[i] = vis[i ^ 1] = 1;
        else vis[i] = 1;
        if(used[v] == -1){
            ans = true;
            return;
        }
        Dfs(v);
    }
    used[p] = 1;
}

int main(){
    int a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&N,&M1,&M2);
        memset(first,-1,sizeof(first));
        ecnt = 0;
        for(int i = 1; i <= M1; ++i){
            scanf("%d%d",&a,&b);
            add_edge(a,b,1);
            add_edge(b,a,1);
        }
        for(int i = 1; i <= M2; ++i){
            scanf("%d%d",&a,&b);
            add_edge(a,b,2);
        }
        memset(used,0,sizeof(used));
        memset(vis,false,sizeof(vis));
        ans = false;
        for(int i = 1; i <= N; ++i) if(!used[i]){
            Dfs(i);
            if(ans) break;
        }
        if(ans) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
View Code

 

1010题 hdu 5223:数学题

  题意:给出若干个区间 gcd 答案,让你恢复出这个数列。

  想法比较自然,首先给数列里的每个数赋为1。

  对于每个询问,把 gcd 答案和区间里每一个数做 lcm ,并把 lcm 赋值给数列里的数。(因为区间里的每个数是该 gcd 的倍数。)

  最后检查一下该数列是否符合每个询问的答案即可。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;

int T;
int N,Q;
int l[1010],r[1010],ans[1010];
ll v[1010];

ll Gcd(ll a,ll b){
    while(a > 0 && b > 0){
        if(a > b) a %= b;
        else b %= a;
    }
    return a + b;
}

ll Lcm(ll a,ll b){
    return a / Gcd(a,b) * b;
}

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&Q);
        for(int i = 1; i <= N; ++i) v[i] = 1;
        for(int i = 1; i <= Q; ++i){
            scanf("%d%d%d",&l[i],&r[i],&ans[i]);
            for(int j = l[i]; j <= r[i]; ++j){
                v[j] = Lcm(v[j],ans[i]);
            }
        }
        //for(int i = 1; i <= N; ++i)
        //    printf("%lld ",v[i]);
        //puts("");
        bool flag = true;
        for(int i = 1; i <= Q; ++i){
            ll cur = v[l[i]];
            for(int j = l[i] + 1; j <= r[i]; ++j) cur = Gcd(cur,v[j]);
            if(cur != ans[i]){
                flag = false;
                break;
            }
        }
        if(flag == false) printf("Stupid BrotherK!\n");
        else{
            printf("%I64d",v[1]);
            for(int i = 2; i <= N; ++i) printf(" %I64d",v[i]);
            puts("");
        }
    }
    return 0;
}
View Code

 

posted @ 2015-05-07 20:23  Naturain  阅读(198)  评论(0编辑  收藏  举报