ACM-ICPC 2018 南京赛区网络预赛 A签到 E状压dp I回文自动机维护回文串值 J 预处理 L 分层图最短路

版权声明:本文为博主原创文章,未经博主允许也可以转载。 https://blog.csdn.net/FrankAx/article/details/82290686

A
思路:输出n-1
Code:

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
int main(){
    int T;
    cin >> T;
    LL n ; 
    while( T-- ){
        cin >> n ;
        cout << n - 1 << endl;
    }
    return 0 ; 
}

E
题意:n道题,每个题有一个ai,bi,t时刻做出来第i题可以获得t*ai + bi分,完成每道题都会给出相应的前提需要完成的题,求能够获得的最大分数。
思路:n只有20,可以状态压缩。先预处理出0到(1<<20)每个数中1的个数就是完成这道题需要的时间。
根据输入的前提需要完成的题目可以给每道题附一个值,为这个值时才能完成这个题。
dp[i]表示完成题目和为i时的最大分数,那么:
当i值包含1<

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
const int AX = (1 << 20) + 66 ;
LL b[AX]; 
LL a[AX]; 
int n ; 
LL num[AX];  
LL dp[AX]; 
int val[AX];
int x ;
int main(){
    memset( num , 0LL , sizeof(num) ) ;
    for( int i = 0 ; i < AX ; i ++ ){
        int tmp = i ; 
        while ( tmp ){
            if( tmp & 1 ) num[i] ++;
            tmp >>= 1 ;
        }
    }
    int cnt ; 
    scanf("%d",&n);
    for( int i = 0 ; i < n ; i++ ){
        scanf("%lld%lld%d",&a[i],&b[i],&cnt) ;
        val[i] = 0 ; 
        for( int j = 0 ; j < cnt ; j++ ){
            scanf("%d",&x); x -- ;
            val[i] += ( 1 << x ) ;
        }
    }

    memset( dp , -1 , sizeof(dp) ) ;
    for( int i = 0 ; i < n ; i++ ){
        if( !val[i] ){
            dp[(1<<i)] = a[i] + b[i];
        }
    }
    LL res = 0LL ;
    for( int i = 0 ; i < ( 1 << n ) ; i++ ){
        for( int j = 0 ; j < n ; j ++ ){
            if( i & ( 1 << j ) ){
                if( ( i ^ val[j] ) == ( i - val[j] ) ){
                    if( dp[(i^(1<<j))] != -1 ){
                        dp[i] = max( dp[i] , dp[(i^(1<<j))] + num[i] * a[j] + b[j] ) ;
                        res = max( res , dp[i] ) ;
                    }
                }
            }
        }
    }
    printf("%lld\n",res);
    return 0 ;
}

I
题意:给一个字符串,找出所有本质不同的回文子串相加模1e9+7。
思路:回文自动机,维护val。
每次添加时会产生新回文串的条件是产生新节点
这里写图片描述
每条边是在回文串两边加上的字符,len[i]是回文串长度,那么值就是:
val[now] = (10^len[i] * id % mod + val[i] ) * 10 + id ;
惭愧,学的时候以为理解了,结果过了几个月全忘了。。。
Code:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 2e6+666;
char a[AX];
char b[AX];
LL po[AX];
LL res;
const int MOD = 1e9+7;
struct Ptree{
    int next1[AX][10];
    int fail[AX];
    LL cnt[AX];
    int num[AX];
    int len[AX];
    int val[AX]; // 回文值
    int s[AX];
    int last;
    int n , p ;

    int newnode( int l ){
        for( int i = 0 ; i < 10 ; i++ ){
            next1[p][i] = 0 ;
        }
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = l ;
        val[p] = 0 ;
        return p++;
    }

    void init(){
        p = 0 ; 
        newnode( 0 );
        newnode( -1 );
        last = 0 ;
        n = 0 ; 
        s[n] = -1;
        fail[0] = 1;
    }

    int get_fail( int x ){
        while( s[ n - len[x] - 1 ] != s[n] ){
            x = fail[x];
        }
        return x ;
    }

    void insert( char c ){
        int id = c - '0';
        s[++n] = id;
        int cur = get_fail( last );
        if( !next1[cur][id] ){
            int now = newnode( len[cur] + 2 );
            fail[now] = next1[get_fail(fail[cur])][id];
            next1[cur][id] = now;

            val[now] = (( po[len[cur]] * id % MOD + val[cur] ) * 10 + id ) % MOD ;
        }
        last = next1[cur][id];
        cnt[last]++;
    }

    LL count(){
        LL ans = 0LL;
        for( int i = p - 1 ; i >= 0 ; i-- ){
            cnt[fail[i]] += cnt[i];
        }
        for( int i = 0 ; i < p ; i ++ ){
            ans += val[i] ; 
            ans %= MOD ;
        }
        return ans ;
    } 
};
Ptree A ;

int main(){
    po[0] = 1 ; 
    for( int i = 1 ; i <= 2e6 ; i++ ){
        po[i] = 10LL * po[i-1] % MOD;
    }
    res = 0 ;
    scanf("%s",a);
    A.init();
    int lena = strlen(a);
    for( int i = 0 ; i < lena ; i ++ ){
        A.insert(a[i]);
    }
    res = A.count();
    printf("%lld\n",res);
    return 0 ;
}

J
题意:将一个数分解成两个数a,b相乘,要求a,b均不为平方数且不为平方数的倍数,求1-n共有多少分解方式。
思路:预处理出2e7的所有满足条件的a,b,记录其前缀和sum[i](到i共有多少个满足条件的数)。然后输出的时候统计下两个数相乘比n小的数的组合就行了。
Code:

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
const int AX = 2e7+1;
bool a[AX];
int sum[AX];
int b[AX];
int tot ;
void init(){
    tot = 0 ;
    memset( a, true , sizeof(a) );
    for( int i = 2 ; i * i < AX ; i++ ){
        int k = i * i ; 
        for( int j = k ; j < AX ; j += k ){
            a[j] = false;
        }
    }
    memset( sum , 0 , sizeof(sum) ) ;
    for( int i = 1 ; i < AX ; i++ ){
        if( a[i] ) { sum[i] = sum[i-1] + 1 ; b[tot++] = i; }
        else sum[i] = sum[i-1];
    }
} 

int main(){
    int T;
    init();
    scanf("%d",&T);
    int n ; 
    while ( T-- ){
        scanf("%d",&n);
        LL res = 0LL ;
        for( int i = 0 ; i < tot && b[i] <= n ; i++ ){
            int tmp = (int)(n / b[i]) ; 
            res += sum[tmp] ; 
        }
        printf("%lld\n",res);
    }
    return 0 ;
}

L
题意:在图上,有k次机会可以直接通过一条边而不计算边权,问起点与终点之间的最短路径。
思路:类似于求最短路经,但由于可以有k次机会不计算边权,所以需要加上一维状态表示用了几次机会。
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long 
using namespace std;
const int AX = 2e5+66;
int s , t ; 
int n , m , k ; 

struct Edge{
    int v ;
    LL w ; 
    int next1 ; 
}G[AX];
int head[AX];
int tot ;
void addEdge( int u , int v , LL w ){
    G[tot].v = v ; G[tot].w = w ;  G[tot].next1 = head[u];
    head[u] = tot++ ; 
}

struct Node{
    int v ;
    LL dis ; 
    int level ; 
    bool operator < ( const Node &x ) const{
        return x.dis < dis ; 
    }
};
priority_queue<Node> q; 
bool vis[AX][20];
LL dis[AX][20];
void dji(){
    while( !q.empty() ){
        Node tmp = q.top();
        q.pop();
        int v = tmp.v;
        int level = tmp.level; 
        if( vis[v][level] ) continue;
        vis[v][level] = true ;
        for( int i = head[v] ; ~i ; i = G[i].next1 ){
            int to = G[i].v ;
            LL w = G[i].w;
            if( w + dis[v][level] < dis[to][level] ){
                dis[to][level] = w + dis[v][level];
                q.push((Node){to,dis[to][level],level});
            }
            if( level < k && dis[v][level] < dis[to][level+1] ){
                dis[to][level+1] = dis[v][level];
                q.push((Node){to,dis[to][level+1],level+1});
            }
        }
    }
}

int main(){
    int T;
    scanf("%d",&T);
    int x , y  ;
    LL w ; 
    while( T-- ){
        tot = 0 ; 
        memset( head , -1 , sizeof(head) ) ;
        scanf("%d%d%d",&n,&m,&k);
        s = 1 ; 
        t = n ; 
        for( int i = 0 ; i < m ; i++ ){
            scanf("%d%d%lld",&x,&y,&w); 
            addEdge( x , y , w ) ;
        }
        memset( vis , false , sizeof(vis) ) ;
        for( int i = 1 ; i <= n ; i++ ){
            for( int j = 0 ; j <= k ; j++ ){
                dis[i][j] = INF;
            }
        }
        dis[s][0] = 0 ; 
        q.push((Node){s,0,0});
        dji();
        LL res = dis[t][0];
        for( int i = 1 ; i <= k ; i++ ){
            res = min( res , dis[t][i] );
        }
        printf("%lld\n",res);
    } 
    return 0 ; 
}

猜你喜欢

转载自blog.csdn.net/FrankAx/article/details/82290686