2015长春网络赛(HDU5437,5438,5439,5441,5442,5443,5444,5446)

来源:互联网 发布:net域名为什么不值钱 编辑:程序博客网 时间:2024/06/06 03:59

2015长春网络赛 ACM/ICPC Asia Regional Changchun Online

解题报告

  • 打星题赛后做出

1001 Alisha’s Party(HDU 5437)

题意:

某Party女主人邀请了一些客人,客人有三个属性:名字,第几个到达,所带礼物价值。
输入(ti,pi),表示当有ti人到达时,女主人去将客人带进来,由于屋子小,每次只能带pi人进入,优先将礼物价值高的且已经到达的客人带进屋子,若礼物价值相同则将先来的客人带入,最后会将所有人带入。
现在求第i个进入屋子的客人名字。

思路:

可以用优先队列或者set模拟,注意行末不要空格,测试结束要空行(4次PE的痛

代码:

窝是用优先队列写的,把注释的上一行换成注释是用set写的版本。

/** @author FreeWifi_novicer* language : C++/C*/#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<map>#include<set>#include<vector>#include<queue>using namespace std;#define clr( x , y ) memset(x,y,sizeof(x))#define cls( x ) memset(x,0,sizeof(x))#define mp make_pair#define pb push_backconst int maxc = 205 ;const int maxk = 150000 + 50 ;struct Fri{    char name[maxc] ;    int id , val ;    bool operator < ( const Fri &a )const{        if( val != a.val )            return val < a.val ;            //return val > a.val ;        return id > a.id ;        //return id < a.id ;    }} ;//set<Fri> f ;priority_queue<Fri>f ;map<int , int> h ;int Q[maxk] , ans[maxk] ;Fri F[maxk] ;int main(){    //freopen("input.txt","r",stdin);    int t ; cin >> t ;    while( t-- ){        int k , m , q ;        cin >> k >> m >> q ;        for( int i = 1 ; i <= k ; i++ ){            Fri A ;            A.id = i ;            scanf( "%s %d" , A.name , &A.val ) ;            F[i] = A ;        }        for( int i = 1 ; i <= m ; i++ ){            int open , num ;            scanf( "%d%d" , &open , &num ) ;            h[open] += num ;        }        for( int i = 1 ; i <= q ; i++ ){            scanf( "%d" , Q+i ) ;        }        int idx = 1 ;        for( int i = 1 ; i <= k ; i ++ ){            f.push( F[i] ) ;            //f.insert( F[i] ) ;            while( !h.empty() && h.begin()->first == i ){                int num = h.begin()->second ;                while( !f.empty() && num-- ){                    ans[idx++] = f.top().id ;                    //ans[idx++] = f.begin()->id ;                    f.pop() ;                    //f.erase( f.begin() ) ;                }                h.erase( h.begin() ) ;            }        }        while( !f.empty() ){            ans[idx++] = f.top().id ;            //ans[idx++] = f.begin()->id ;            f.pop();            //f.erase( f.begin() ) ;        }        for( int i = 1 ; i <= q ; i++ ){            if( i != q )                printf( "%s " , F[ans[Q[i]]].name ) ;            else                printf( "%s" , F[ans[Q[i]]].name ) ;        }        puts("") ;    }    return 0;}

1002 Ponds(HDU 5438)

题意:

删去图中度小于2的节点,删到不能删为止,然后求点数为奇数的连通块的价值和。

思路:

bfs或dfs删去节点,再来一次bfs或dfs统计价值。

代码:

/** @author FreeWifi_novicer* language : C++/C*/#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<map>#include<set>#include<vector>#include<queue>using namespace std;#define clr( x , y ) memset(x,y,sizeof(x))#define cls( x ) memset(x,0,sizeof(x))#define mp make_pair#define pb push_backtypedef long long lint;typedef long long ll;typedef long long LL;const int maxn = 1e4 + 50 ;vector<int> G[maxn] ;int val[maxn] , deg[maxn] ;int cnt ;lint sum ;bool vis[maxn] ;void dfs( int u , int &cnt , lint &sum ){    sum += val[u] ;    cnt ++ ;    vis[u] = true ;    for( int i = 0 ; i < int( G[u].size() ) ; i++ ){        int v = G[u][i] ;        if( vis[v] ) continue ;        dfs( v , cnt , sum ) ;    }}queue<int>q ;int main(){    //freopen("input.txt","r",stdin);    int t ; cin >> t ;    while( t-- ){        int n , m ; cin >> n >> m ;        for( int i = 0 ; i < n ; i++ ){            G[i].clear() ;            scanf( "%d" , val+i ) ;        }        cls( deg ) ;        for( int i = 1 ; i <= m ; i++ ){            int u , v ;            scanf( "%d%d" , &u , &v ) ;            G[u-1].pb(v-1) ;            G[v-1].pb(u-1) ;            deg[u-1] ++ , deg[v-1] ++ ;        }        for( int i = 0 ; i < n ; i++ )             if( deg[i] < 2 ) q.push( i ) ;        cls( vis ) ;        while( !q.empty() ){            int v = q.front() ;             deg[v] = 0 , vis[v] = true ;            for( int i = 0 ; i < int( G[v].size() ) ; i++ ){                int u = G[v][i] ;                if( --deg[u] < 2 && !vis[u] ) q.push( u ) ;            }            q.pop() ;        }        lint ans = 0 ;        for( int i = 0 ; i < n ; i++ ){            if( vis[i] ) continue ;            sum = 0 , cnt = 0 ;            dfs( i , cnt , sum ) ;            if( cnt & 1 ) ans += sum ;        }        printf( "%I64d\n" , ans ) ;    }    return 0;}

*1003 Aggregated Counting(HDU 5439)

题意:

写一个序列,序列前两位是1,2,后面陆续由i个第i位的数字构成(一开始只有1),就变成了1,2,2,3,3,4,4,4,5,5,5,6,6,6,6,…
现在让你求该序列的前n项和。

思路:

喜闻乐见的打表找规律
我们用 ai 表示第i个位置对应的数字。

firsti 表示第i个数字对应的起始位置,比如说first1=1first2=2first3=4...

以及用 cnti 表示第i个数字的数量,比如说cnt1=1cnt2=2cnt3=2...

可以发现:
ansn=1+2+2+3+3+4+4+4+5+...+an

进而转化为这样的形式:
ansn=11+2(2+3)+3(4+5)+4(6+7+8)+...+x(firstx+(firstx+1)+(firstx+2)+..+n)
比如说:
ans7=11+2(2+3)+3(4+5)+4(6+7)=90

可以发现答案由数字乘以其对应起始位置构成的,长度为 cnti 的等差数列和构成。即:
ansn=firsti<ni=1(i(2firsti+cnti1)(cnti)/2)+nj=firsti+1((i+1)j)

那么我们先预处理出每一个数字的firsticntiansi,查询时只需要二分查找n所在的位置,若n正好处于一个firstx的位置,那么直接输出ansi1+firstx,若不是,那么累加一下就行;

注意等差数列求和公式要用到逆元。
预处理复杂度为O(n) , 查询复杂度为O(logn)

代码:

/** @author FreeWifi_novicer* language : C++/C*/#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<map>#include<set>#include<vector>#include<queue>using namespace std;#define clr( x , y ) memset(x,y,sizeof(x))#define cls( x ) memset(x,0,sizeof(x))#define mp make_pair#define pb push_backtypedef long long lint;typedef long long ll;typedef long long LL;const lint maxlen = 1e6 * 5 + 50 ;const lint two = 1e8 * 5 + 4 ;const lint maxn = 1e9 + 1e6 ;const lint mod = 1e9 + 7 ;int cnt[maxlen] , a[maxlen] ; int num ;lint ans[maxlen] , first[maxlen] ;void init(){    a[1] = 1 , a[2] = 2 , a[3] = 2 ;    cls( cnt ) ;    cnt[1] = 1 , cnt[2] = 2 ;    first[1] = 1 , first[2] = 2 ;    lint pos1 = 4 , pos2 = 3 ;    for( int i = 3 ; pos2 < maxn ; i++ ){        cnt[i] = a[i] ;        pos2 += cnt[i] ;        first[i] = pos1 ;        for( int j = pos1 ; j <= min( pos2 , maxlen ) ; j++ )            a[j] = i ;        pos1 = pos2 + 1 ;    }    ans[1] = 1 ;    num = 2 ;    lint end = 2 ;    lint tmp = 1 ;    while( end <= maxn ){        end = min( maxn , first[num] + cnt[num] - 1 ) ;        tmp = ( tmp + ( num * ( first[num] + end ) % mod ) % mod * ( cnt[num] ) % mod * two % mod ) % mod ;        ans[num] = tmp ;        end ++ , num ++ ;    }}lint solve( int n ){    int pos = lower_bound( first + 1 , first + num , n ) - first ;    lint res ;    if( first[pos] == n  ){        res = ( pos * first[pos] % mod + ans[pos-1] ) % mod ;        return res ;    }    res = ans[pos-2] ;    lint end = first[pos-1] ;    while( end <= n ){        res = ( res + end * ( pos - 1 ) % mod ) % mod ;        end ++ ;    }    return res ;}int main(){//  freopen("input.txt","r",stdin);    init() ;    int t ; cin >> t ;    while( t-- ){        int n ; cin >> n ;        cout << solve( n ) << endl ;    }    return 0;}

1005 Travel(HDU 5441)

题意:

有q次询问,求路径每条边权值不超过d的起始点二元组数目,(s,t)与(t,s)算两次。

思路:

对每一条边按权值排序,然后用并查集划分出权值不超过d的连通块,统计出连通块节点数num,答案就是2C2num(因为算两次)。

代码:

/** @author FreeWifi_novicer* language : C++/C*/#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<map>#include<set>#include<vector>#include<queue>using namespace std;#define clr( x , y ) memset(x,y,sizeof(x))#define cls( x ) memset(x,0,sizeof(x))#define mp make_pair#define pb push_backtypedef long long lint;typedef long long ll;typedef long long LL;const int maxn = 20000 + 50 ;const int maxm = 100000 + 50 ;const int maxq = 5000 + 50 ;lint ans[maxm] ;int rk[maxn] , num[maxn] , fa[maxn] ;struct Query{    int id , d ;    bool operator < ( const Query &a )const{        if( d != a.d ) return d < a.d ;        return id < a.id ;    }}Q[maxq] ;struct Edge{    int u , v , w ;    bool operator < ( const Edge &a )const{        if( w != a.w ) return w < a.w ;        return u < a.u ;    }}E[maxm] ;int find( int x ){    return fa[x] == x ? x : find( fa[x] ) ;}lint res ;void merge( int x , int y ){    x = find( x ) , y = find( y ) ;    if( x == y ) return ;    res += num[x] * num[y] ;    if( rk[x] < rk[y] )        fa[x] = y , num[y] += num[x] , num[x] = 0;    else        fa[y] = x , num[x] += num[y] , num[y] = 0;    if( rk[x] == rk[y] ) rk[x] ++ ;}void init( int n ){    cls( ans ) ;    res = 0 ;    for( int i = 1 ; i <= n ; i++ ){        rk[i] = 0 , fa[i] = i , num[i] = 1 ;    }}void solve( int q , int m ){    for( int j = 0 , i = 0 ; i < q ; i++ ){        int tmp = Q[i].d ;        while( j < m  ){            if( E[j].w <= tmp )                merge( E[j].u , E[j].v ) ;            else                break ;            j++ ;        }        ans[Q[i].id] = res ;    }}int main(){    //freopen("input.txt","r",stdin);    int t ; cin >> t ;    while( t-- ){        int n , m , q ;        cin >> n >> m >> q ;        for( int i = 0 ; i < m ; i++ ){            scanf( "%d%d%d" , &E[i].u , &E[i].v , &E[i].w ) ;        }        sort( E , E + m ) ;        for( int i = 0 ; i < q ; i++ ){            scanf( "%d" , &Q[i].d ) ;            Q[i].id =  i ;        }        sort( Q , Q + q ) ;        init( n ) ;        solve( q , m ) ;        for( int i = 0 ; i < q ; i++ ) printf( "%I64d\n" , ans[i] * 2 ) ;    }    return 0;}

1006 Favorite Donut(HDU 5442)

题意:

一个字符环,求字典序最大的起始位置与方向

思路:

一般字符环问题都可以直接想到最小表示法,这题的标准解法也应该是最小表示法。
也可以直接后缀数组统计,逆时针顺时针都跑一遍,再O(n)比较即可。
队友擅长用后缀数组,并且模板高度可靠,所以理所当然后者了~

代码:

偷偷把队友代码贴了上来

#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<iostream>#include<algorithm>#include<vector>#include<map>#include<queue>#include<stack>#include<string>#include<map>#include<set>#include<ctime>#define eps 1e-6#define LL long long#define pii pair<int, int>//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;const int maxn = 200000;//const int INF = 0x3f3f3f3f;int n;int idx[maxn], pos[maxn];struct SuffixArray {      int s[maxn];          int sa[maxn];        int rank[maxn];     int height[maxn];    int t[maxn], t2[maxn], c[maxn];    int n;    void clear() { n = 0; memset(sa, 0, sizeof(sa)); }      void build_sa(int m) {           int i, *x = t, *y = t2;          for(i = 0; i < m; i++) c[i] = 0;          for(i = 0; i < n; i++) c[x[i] = s[i]]++;          for(i = 1; i < m; i++) c[i] += c[i-1];          for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;          for(int k = 1; k <= n; k <<= 1) {                int p = 0;                for(i = n-k; i < n; i++) y[p++] = i;                for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;                for(i = 0; i < m; i++) c[i] = 0;                for(i = 0; i < n; i++) c[x[y[i]]]++;                for(i = 0; i < m; i++) c[i] += c[i-1];                for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];                swap(x, y);                p = 1; x[sa[0]] = 0;                for(i = 1; i < n; i++)                    x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k] ? p-1 : p++;                if(p >= n) break;                m = p;          }        }        void build_height() {          int i, j, k = 0;          for(i = 0; i < n; i++) rank[sa[i]] = i;          for(i = 0; i < n; i++) {              if(k) k--;                j = sa[rank[i]-1];                while(s[i+k] == s[j+k]) k++;                height[rank[i]] = k;          }        }} sa;void add_char(int i, int x, int p) {     idx[sa.n] = x;    pos[sa.n] = p;      sa.s[sa.n++] = i;  }  char str[30000];int main() {    //freopen("input.txt", "r", stdin);    int T; cin >> T;     while(T--) {        cin >> n;        sa.clear();        scanf("%s", str);        int len = strlen(str);        for(int i = 0; i < len; i++) add_char(str[i]-'a'+2, 0, i+1);                for(int i = 0; i < len; i++) add_char(str[i]-'a'+2, 0, i+1);        add_char(1, 2, n+5);        for(int i = len-1; i >= 0; i--) add_char(str[i]-'a'+2, 1, i+1);        for(int i = len-1; i >= 0; i--) add_char(str[i]-'a'+2, 1, i+1);        add_char(1, 2, n+2);        add_char(0, 2, n+5);        sa.build_sa(40);        sa.build_height();        int ans = pos[sa.sa[sa.n-1]], dir = idx[sa.sa[sa.n-1]];        //cout << ans << endl;        for(int i = sa.n-1; i>=1; i--) {            if(sa.height[i] < n) break;            else {                if(pos[sa.sa[i-1]]<ans) {                    ans = pos[sa.sa[i-1]];                    dir = idx[sa.sa[i-1]];                }                else if(pos[sa.sa[i-1]]==ans && idx[sa.sa[i-1]]<dir) {                    ans = pos[sa.sa[i-1]];                    dir = idx[sa.sa[i-1]];                }            }        }        cout << ans << " " << dir << endl;    }    return 0;}

1007 The Water Problem(HDU 5443)

题意:

给n个数对于每个询问(l,r)求区间(l,r)最大值

思路:

史上最良心标题
RMQ/线段树。咦,好像AC了?

代码:

/** @author FreeWifi_novicer* language : C++/C*/#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<map>#include<set>#include<vector>#include<queue>using namespace std;#define clr( x , y ) memset(x,y,sizeof(x))#define cls( x ) memset(x,0,sizeof(x))#define mp make_pair#define pb push_back#define lson l , mid , rt << 1 #define rson mid + 1 , r , rt << 1 | 1 typedef long long lint;typedef long long ll;typedef long long LL;const int maxn = 200000 + 50 ;int ma[4 * maxn];void push_up( int rt ){    ma[rt] = max( ma[rt << 1] , ma[rt << 1 | 1]) ; }void build( int l , int r , int rt ){    if( l == r ){        scanf( "%d" , &ma[rt] ) ;        return ;    }    int mid = ( l + r ) / 2;    build( lson ) ;    build( rson ) ;    push_up( rt );}int query( int ql , int qr , int l , int r , int rt ){    if( ql > r || qr < l ) return 0 ;    if( ql <= l && qr >= r ) return ma[rt] ;     int res = -1 ;    int mid = ( l + r ) / 2 ;    res = max( query( ql , qr , lson ) , query( ql , qr , rson ) ) ;    return res ;}int main(){    //freopen("input.txt","r",stdin);    int t ; cin >> t ;    while( t-- ){        int n ; cin >> n ;        clr( ma , -1 ) ;        build( 1 , n , 1 );        int m ; cin >> m ;        for( int i = 1 ; i <= m ; i++ ){            int l , r ;            scanf( "%d%d" , &l , &r ) ;            cout << query( l , r , 1 , n , 1 ) << endl ;        }    }    return 0;}

1008 Elven Postman(HDU 5444)

题意:

故事背景大概就是。。让你建立一颗二叉搜索树,然后输出某个值的搜索路径

思路:

建树+输出dfs搜索路径。
原来数据结构学过。。但没真正写过,贵在队友强大啊cry。

代码:

赛后自己重写的,参考http://blog.csdn.net/lwt36/article/details/48415647#t5

/** @author FreeWifi_novicer* language : C++/C*/#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<string>#include<map>#include<set>#include<vector>#include<queue>using namespace std;#define clr( x , y ) memset(x,y,sizeof(x))#define cls( x ) memset(x,0,sizeof(x))#define mp make_pair#define pb push_backtypedef long long lint;typedef long long ll;typedef long long LL;const int maxn = 1005 ;int lc[maxn] , rc[maxn] , val[maxn] ;int cnt ;int newnode( int x ){    cnt ++ ;    lc[cnt] = rc[cnt] = 0 ;    val[cnt] = x ;    return cnt ;}int insert( int rt , int x ){    if( !val[rt] ){        return newnode( x ) ;    }    if( x > val[rt] ){        lc[rt] = insert( lc[rt] , x ) ;    }    else {        rc[rt] = insert( rc[rt] , x ) ;    }    return rt ;}void dfs( int rt , int x ){    if( x > val[rt] ){        putchar( 'W' ) ;        dfs( lc[rt] , x ) ;    }    else if( x < val[rt] ){        putchar( 'E' ) ;        dfs( rc[rt] , x ) ;    }}int main(){    //freopen("input.txt","r",stdin);    int t ; cin >> t ;    while( t-- ){        int n ; cin >> n ;        cnt = 0 ;        cls( val ) ;        cls( lc ) ;        cls( rc ) ;        for( int i = 0 ; i < n ; i++ ){            int x ;            scanf( "%d" , &x ) ;            insert( 1 , x ) ;        }        int q ; cin >> q ;        for( int i = 0 ; i < q ; i++ ){            int x ;            scanf( "%d" , &x ) ;            dfs( 1 , x ) ;            puts( "" ) ;        }    }    return 0;}

*1010 Unknown Treasure(HDU 5446)

题意:

输入 n,m,q,以及 q 个不同素数 pi

Cmn%(p1p2...pq)

1nm1018q10pi105

qi=11018.

思路:

lucas + CRT 模板题,但由于会出现相乘爆long long的情况,需要手写模除乘法,这个时候就要拼模板的鲁棒性了。
这题没A,是因为我坑队友,把求逆元公式写反了。。关键TMD样例居然都过了

代码:

这个lucas模板比较慢,但贵在写起来简单。
用纯C写是因为之前听说GCC 4.7有_int128的黑科技。。

#include <stdio.h>#include <string.h>#include <stdlib.h>#define maxn 12typedef unsigned long long lint ;lint pr[maxn] , num[maxn] ;lint multi_mod( lint a , lint b , lint p ) {    a %= p , b %= p ;    lint ret = 0;    while( b ) {        if( b & 1 ) {            ret += a;            if( ret >= p ) ret -= p ;        }        a <<= 1;        if( a > p ) a -= p;        b >>= 1;    }    return ret;}lint fast_pow( lint x , lint n , lint p ){    lint res = 1 ;    x %= p ;    while( n ) {        if( n & 1 )            res = multi_mod( res , x , p ) ;        n >>= 1 ;        x = multi_mod( x , x , p ) ;    }    return res ;}lint quick_pow( lint x , lint n , lint p ){    lint res = 1 ;    x %= p ;    while( n ) {        if( n & 1 )            res = res * x % p ;        n >>= 1 ;        x = x * x % p ;    }    return res ;}lint C( lint n , lint m , lint p ){    if( n < m ) return 0 ;    lint ans = 1 ;    for( int i = 1 ; i <= m ; i++ ){        lint a = ( n + i - m ) % p ;        lint b = i % p ;        ans = ans * ( a * quick_pow( b , p - 2 , p ) % p ) % p ;    }    return ans ;}lint lucas( lint n , lint m , lint p ){    if( m == 0 ) return 1 ;    return C( n % p , m % p , p ) * lucas( n / p , m / p , p ) % p ;}lint CRT( int n ){    if( n == 1 ) return num[0] ;    lint M = 1 ;    lint res = 0 ;    for( int i = 0 ; i < n ; i++ ) M *= pr[i] ;    for( int i = 0 ; i < n ; i++ ){        lint x ;        lint tmp = M / pr[i] ;        x = fast_pow( tmp , pr[i] - 2 , pr[i] ) ;        res = ( res + multi_mod( multi_mod( x , tmp , M ) , num[i] , M ) ) % M ;    }    return res ;}int main(){    //freopen( "input.txt" , "r" , stdin ) ;    lint n , m ;    int t , T ;    scanf( "%d" , &T ) ;    while( T-- ){        scanf( "%I64u%I64u%d" , &n , &m , &t ) ;        int i ;        for( i = 0 ; i < t ; i++ ){            scanf( "%I64u" , pr + i ) ;            num[i] = lucas( n , m , pr[i] ) ;        }        lint ans = CRT( t ) ;        printf( "%I64u\n" , ans ) ;    }}

赛后小结:

这场比赛的发挥,前期逆风,中期稳扎稳打,后期有点遗憾。
对于我们队伍而言,最理想的状况应该是:
1001.一开始没有迷之PE4次(这个主要还是评测太坑;
1002.我读清题意再去写(WA3发才发现。。
1003.是可做题,且极适合我队风格,但当时没发现,或者说被上述问题把时间都硬生生耗光了。
1010.狗哥套模板测大数据算出0其实是正解,但我们居然赛后才想清楚,如果当时想清楚直接交一发没准有意外之喜;以及后来我写个CRT把求逆元正反位置写错了,简直把整个队都坑哭了。。

总的来说这次比赛,前期的不顺在中期状态稳定后没影响太多,后来1010也隐隐要有AC之势。。如果窝不逗比的话。所以说要改进的地方大概还是提升自己实力为主吧,像这次图论题是简单题,而我最差的应该就是图论了(咦,难道有不差的?),所以当队友都卡住的情况下,我的发挥真的让人不敢恭维啊。。如果不是中期的换题,其实还是很危险的。so以后尽量还是多思考的前提下多多交流吧:P
那么下一场比赛加油啦~

0 0
原创粉丝点击