INOI选做...

退役选手复健

Free ticket
题解:
floyd之后直接找最大值。

/**
https://www.codechef.com/INOIPRAC/problems/INOI1402
*/
#include
#define ll long long
using namespace std;
const int INF = 1e9 + 4;
const int MAXN = 3e2 + 4;
int mp[MAXN][MAXN];
int N,M;
int w33ha() {
    for(int i=1; i<=N; i++) {
        for(int j=1; j<=N; j++) {
            if(i != j) {
                mp[i][j] = INF;
            } else
                mp[i][j] = 0;
        }
    }
    for(int i=1; i<=M; i++) {
        int u, v, w;
        scanf("%d%d%d",&u,&v,&w);
        mp[u][v] = min(mp[u][v], w);
        mp[v][u] = min(mp[v][u], w);
    }
    for(int k=1; k<=N; k++)
        for(int i=1; i<=N; i++)
            for(int j=1; j<=N; j++)
                mp[i][j] = min(mp[i][j], mp[i][k]+mp[k][j]);
    vector<int> o;
    for(int i=1; i<=N; i++)
        for(int j=1; j<=N; j++)
            if(i!=j) {
                o.push_back(mp[i][j]);
            }
    sort(o.begin(), o.end(), greater<int>());
    printf("%d\n", *o.begin());
    return 0;
}
int main() {
    while(scanf("%d%d",&N, &M)!=EOF)
        w33ha();
    return 0;
}

Triathlon
题解:
后面两个比赛都可以同时进行,所以时间可以合起来算。
每个人的结束时间就是(在之前的人使用电脑的总时间+当前这个人进行其他比赛时间)
要让最大结束时间最小,那么考虑贪心,A.c,B.c是用电脑的时间,A.o,B.o是其他比赛的时间。那么A要在前面就要满足

max(A.c + A.o, A.c + B.c + B.o) < max(A.c + B.c + A.o, B.c + B.o)

即可。

/**
https://www.codechef.com/INOIPRAC/problems/INOI1201
*/
#include
#define ll long long
using namespace std;
const int MAXN = 2e5 + 4;
struct Ds {
    int c, o;
} a[MAXN];
inline bool dex(Ds A, Ds B) {
    return max(A.c + A.o, A.c + B.c + B.o) < max(A.c + B.c + A.o, B.c + B.o);
}
int N;
int w33ha() {
    for(int i=1; i<=N; i++) {
        int x, y, z;
        scanf("%d%d%d",&x,&y,&z);
        a[i].c = x ;
        a[i].o = y + z;
    }
    sort(a + 1, a + N + 1, dex);
    ll ans = 0, rest = 0;
    for(int i=1; i<=N; i++) {
        ans = max(ans, rest + a[i].c + a[i].o);
        rest += a[i].c;
    }
    printf("%lld\n",ans);
    return 0;
}
int main() {
    while(scanf("%d",&N)!=EOF)
        w33ha();
    return 0;
}

Calvins Game
题解:DAG最长路,不过权值在点上。
from1: 从1开始往后走到i的点的权值和。
fromk: 从k开始往后走到i的点的最大权值和。
(包含起点终点的)
然后我们就是求最大的

from1[i] - a[i] + fromk[i] - a[k]

还是比较简单的图论题

#include
#define ll long long
using namespace std;
const ll INF = 1e14;
const int MAXN = 1e6 + 4;
ll dis[MAXN];
ll from1[MAXN], fromk[MAXN];
int n,k,a[MAXN];
vector<int>G[MAXN];
short inq[MAXN];
void add(int x,int y) {
    G[x].push_back(y);
}
queue<int>q;
void spfa(int st) {
    for(int i=1; i<=n; i++)
        dis[i] = -INF;
    dis[st] = a[st];
    q.push(st);
    inq[st] = 1;
    while(!q.empty()) {
        int x = q.front();
        q.pop();
        for(int i=0; i<G[x].size(); i++) {
            int to = G[x][i];
            if(dis[to] < dis[x] + a[to]) {
                dis[to] = dis[x] + a[to];
                if(!inq[to]) {
                    inq[to] = 1;
                    q.push(to);
                }
            }
        }
        inq[x] = 0;
    }
}
int w33ha() {
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(int i=1; i<=n; i++) {
        if(i<n)
            add(i,i+1);
        if(i+1<n)
            add(i,i+2);
    }
    spfa(1);
    memcpy(from1, dis, (n+1)*sizeof(ll));
    spfa(k);
    memcpy(fromk, dis, (n+1)*sizeof(ll));
    ll ans = from1[k] - a[k];
    for(int i=k+1; i<=n; i++) {
        ans = max(ans, from1[i] - a[i] + fromk[i] - a[k]);
    }
    printf("%lld\n",ans);
    return 0;
}
int main() {
    while(scanf("%d%d",&n,&k)!=EOF)
        w33ha();
    return 0;
}

Sequence Land
题解:并查集+set判断交集

/**
https://www.codechef.com/INOIPRAC/problems/INOI1302
**/
#include
#define ll long long
using namespace std;
const ll INF = 1e14;
const int MAXN = 304;
int n,k;
set<int> o[MAXN];
int sz[MAXN], f[MAXN];
int Find(int x) {
    return f[x] == x ? x : f[x] = Find(f[x]);
}
int Count(set<int>& A, set<int>& B) {
    int cnt = 0;
    for(auto x:A) {
        if(B.find(x) != B.end()) {
            ++ cnt;
        }
    }
    return cnt;
}
int w33ha() {
    for(int i=1; i<=n; i++) {
        f[i] = i;
        sz[i] = 1;
    }
    for(int i=1; i<=n; i++) {
        o[i].clear();
        int cnt,x;
        scanf("%d",&cnt);
        for(int j=1; j<=cnt; j++) {
            scanf("%d",&x);
            o[i].insert(x);
        }
        for(int j=1; j<i; j++) {
            if(Count(o[i], o[j]) >= k) {
                int x = Find(i), y = Find(j);
                if(x != y) {
                    f[y] = x;
                    sz[x] += sz[y];
                }
            }
        }
    }
    printf("%d\n",sz[Find(1)]);
    return 0;
}
int main() {
    while(scanf("%d%d",&n,&k)!=EOF)
        w33ha();
    return 0;
}

Wealth Disparity
题解:max(子树根节点值 - 子树最小值) ,dfs即可

/**
https://www.codechef.com/INOIPRAC/problems/INOI1601
**/
#include
#define ll long long
using namespace std;
const int INF = 2e9;
const int MAXN = 1e5 + 4;
vector<int> G[MAXN];
int n, st, ans;
int a[MAXN], mint[MAXN];
void dfs(int x) {
    mint[x] = INF;
    for(int i=0; i<G[x].size(); i++) {
        int to = G[x][i];
        dfs(to);
        mint[x] = min(mint[x], mint[to]);
    }
    ans = max(a[x] - mint[x], ans);
    mint[x] = min(mint[x], a[x]);
}
int w33ha() {
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    st = -1;
    for(int i=1; i<=n; i++) {
        int x;
        scanf("%d",&x);
        if(x == -1)
            st = i;
        else {
            G[x].push_back(i);
        }
    }
    ans = -INF;
    dfs(st);
    printf("%d\n",ans);
    return 0;
}
int main() {
    while(scanf("%d",&n)!=EOF)
        w33ha();

    return 0;
}

Table Sum
题解:
用线段树来模拟循环位移即可

/**
https://www.codechef.com/INOIPRAC/problems/INOI1202
**/
#include
#define ll long long
using namespace std;
const int MAXN = 2e5 + 4;
struct tree {
    int l,r,w,tag;
} tr[MAXN << 2];

int v[MAXN], n ;

void build(int k, int l, int r) {
    tr[k].l = l, tr[k].r = r;
    if(l == r) {
        tr[k].tag = 0;
        tr[k].w = v[l];
        return ;
    }
    int mid = (l+r)>>1;
    build(k<<1, l, mid);
    build(k<<1|1, mid+1, r);
    tr[k].w = max(tr[k<<1].w, tr[k<<1|1].w);
}
void pushdown(int k) {
    int gl = (k<<1), gr = (k<<1|1), val = tr[k].tag;
    tr[gl].tag += val;
    tr[gr].tag += val;
    tr[k].tag = 0;
    tr[gl].w += val;
    tr[gr].w += val;
}
void change(int k,int a,int b,int x) {
    int l = tr[k].l, r = tr[k].r;
    if(l == a && r == b) {
        tr[k].tag += x;
        tr[k].w += x;
        return ;
    }
    pushdown(k);
    int mid = (l+r)>>1;
    if(b <= mid)
        change(k<<1,a,b,x);
    else if(a > mid)
        change(k<<1|1,a,b,x);
    else {
        change(k<<1,a,mid,x);
        change(k<<1|1,mid+1,b,x);
    }
    tr[k].w = max(tr[k<<1].w, tr[k<<1|1].w);
}
vector<int> ans;
int w33ha() {
    ans.clear();
    for(int i=1; i<=n; i++) {
        scanf("%d",&v[i]);
        v[i] += i;
    }
    build(1, 1, n);
    ans.push_back(tr[1].w);
    for(int i=1; i<n; i++) {
        change(1,n-i+1,n-i+1,-n+1);
        change(1,1,n-i,1);
        if(n-i+2<=n)
            change(1,n-i+2,n,1);
        ans.push_back(tr[1].w);
    }
    for(int i=0; i<ans.size(); i++) {
        if(i == (int)ans.size()-1)
            printf("%d\n",ans[i]);
        else
            printf("%d ",ans[i]);
    }
    return 0;
}
int main() {
    while(scanf("%d",&n) != EOF)
        w33ha();
    return 0;
}

Special Sums

题解:3种情况

a[i] (i==j)
a[i] + a[j] - sumbFront[i] + sumbFront[j-1] (i<j)
a[i] + a[j] - sumbFront[i-1] + sumbBack[j+1] (i>j)

前缀/后缀和之后,可以枚举i,j的话可以用后缀最大值来代替

/**
https://www.codechef.com/INOIPRAC/problems/INOI1501
*/
#include
#define ll long long
using namespace std;
const int MAXN = 1e6 + 4;
const ll INF = 1e18;
ll bBack[MAXN] , bFront[MAXN];
ll maxBack[MAXN] , maxFront[MAXN];
ll a[MAXN],b[MAXN];
int n;
int w33ha(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
    bFront[0] = 0;
    bBack[n+1] = 0;
    for(int i=1,j=n;i<=n&&j>=1;i++,j--){
        bFront[i] = bFront[i-1] + b[i];
        bBack[j] = bBack[j+1] + b[j];
    }
    maxFront[n+1] = -INF;
    maxBack[n+1] = -INF;
    for(int i=n;i>=1;i--){
        maxFront[i] = max(maxFront[i+1] , a[i] + bFront[i-1]);
        maxBack[i] = max(maxBack[i+1] , a[i] + bBack[i+1]);
    }
    ll ans = a[1];
    for(int i=1;i<=n;i++){
        ans = max(ans , a[i]);
    }
    for(int i=1;i<n;i++){
        ll p1 = a[i] - bFront[i] + maxFront[i+1];
        ll p2 = a[i] + bFront[i-1] + maxBack[i+1];
        //cout << i <<" : " << p1 <<" " << p2 << endl;
        ans = max(ans , max(p1,p2));
    }
    printf("%lld\n",ans);
    return 0;
}

int main(){

    w33ha();

    return 0;
}

Highway Bypass

题解,棋盘dp , f[i][j][k][0/1] 表示从起点到(i,j),已经按照当前方向行走了k步,当前方向是 横着0 / 竖着1 的方案数
注意边界条件。

/**
https://www.codechef.com/INOIPRAC/problems/INOI1401
*/
#include
#define ll long long
using namespace std;
const int mod = 20011;
const int MAXN = 304;
int f[MAXN][MAXN][MAXN][2];
int r,c,d;
int mp[MAXN][MAXN];
int main(){
    memset(f,0,sizeof(f));
    scanf("%d%d%d",&r,&c,&d);
    for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
            scanf("%d",&mp[i][j]);
    if(r == 1&& c == 1) return puts("1"),0;
    for(int i=1;i<=r;i++){
        for(int j=1;j<=c;j++){
            if(mp[i][j] == 0) continue;
            if(i==1&&j==1) continue;
            if(i==1&&j==2){
                f[i][j][1][0] = 1; //cout << i << " _ " << j << " 0"<< endl;
                continue;
            }
            if(i==2&&j==1){
                f[i][j][1][1] = 1; //cout << i << " _ " << j << " 1" << endl;
                continue;
            }
            f[i][j][1][1] = 0;
            f[i][j][1][0] = 0;
            //cout << i << " "<< j << endl;
            for(int k=1;k<=d;k++){
                f[i][j][1][0] += f[i][j-1][k][1];
                f[i][j][1][1] += f[i-1][j][k][0];
                f[i][j][1][0] %= mod;
                f[i][j][1][1] %= mod;
                //cout << f[i][j][1][0] <<" "<< f[i][j][1][1] << endl;
            }
            for(int k=2;k<=d;k++){
                f[i][j][k][0] += f[i][j-1][k-1][0];
                f[i][j][k][0] %= mod;
                f[i][j][k][1] += f[i-1][j][k-1][1];
                f[i][j][k][1] %= mod;
            }
        }
    }
    int ans = 0;
    for(int i=1;i<=d;i++){
        for(int t=0;t<2;t++){
            ans = (f[r][c][i][t]+ans) % mod;

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

Periodic Strings

题解:枚举n的因数,计算每个因数对答案的贡献。我们可以发现,对于长度n,答案是 t [ n ] = 2 n − ∑ n m o d    i   =   0 t [ i ] t[n] = 2^n - \sum_{n \mod i \ = \ 0} t[i] t[n]=2nnmodi = 0t[i] ,递推计算 t [ i ] t[i] t[i]即可
r e s [ 1 ] = 2 res[1] = 2 res[1]=2

/**
https://www.codechef.com/INOIPRAC/problems/INOI1502
*/
#include
#define ll long long
using namespace std;
const int MAXN = 150004;
ll n,m;
vector<ll> vec;
ll t[MAXN];
ll fp(ll x , ll y){
    if(y == 0) return 1LL % m;
    ll temp = fp(x,y/2);
    if(y & 1){
        return temp * temp % m * x % m;
    }
    else {
        return temp * temp % m;
    }
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=2;i<=sqrt(n);i++){
        if(n % i == 0) {
            vec.push_back(i);
            if(n/i!=i)vec.push_back(n/i);
        }
    }
    if(n == 1){
        printf("%lld\n",2LL % m);
        return 0;
    }
    sort(vec.begin() , vec.end());
    if(vec.size() == 0){
        printf("%lld\n",(fp(2LL,n) - (2%m) + m) % m);
        return 0;
    }
    t[vec[0]] = (fp(2LL,vec[0]) - (2%m) + m) % m;
    vec.push_back(n);
    for(int i=1;i<vec.size();i++){
        ll x = vec[i];
        ll sum = (fp(2LL,x) - (2%m) + m) % m;
        for(int i=2;i<=sqrt(x);i++){
            if(x % i == 0){
                sum -= t[i];
                sum = ((sum % m) + m) %m;
                if(x / i != i) sum -= t[x/i];
                sum = ((sum % m) + m) %m;
            }
        }
        t[x] = sum;
    }
    printf("%lld\n",t[n]);
    return 0;
}

1 Road Trips and Museums - INOI 2018
每次做INOI都有一种大脑被弓虽女干的感觉(不对劲
考虑每个联通块就行,dfs求出每个联通块内部的博物馆数量,把联通块排序之后,博物馆数量最多的和最少的联通块交替取即可。

/**
https://www.codechef.com/INOIPRAC/problems/ROADTRIP
*/
#include
#define ll long long
using namespace std;
const int MAXN = 1e6 + 4;
vector<int> G[MAXN];
vector<ll> ans;
int n,m,k,museum[MAXN];
short vis[MAXN];
ll sum;
void dfs(int x){
    vis[x] = 1;
    sum += museum[x];
    for(int i=0;i<G[x].size();i++){
        if(!vis[G[x][i]])dfs(G[x][i]);
    }
}
int w33ha(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;i++)scanf("%d",&museum[i]);
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            sum = 0;
            dfs(i);
            ans.push_back(sum);
        }
    }
    sort(ans.begin(),ans.end(),greater<ll>());
    int l=0,r=(int)ans.size()-1;
    if(ans.size()<k){
        puts("-1");
    } else {
        sum = 0;
        for(int i=0;i<k;i++){
            if(i%2==0)sum += ans[l++];
            else sum += ans[r--];
        }
        printf("%lld\n",sum);
    }
    ans.clear();
    for(int i=1;i<=n;i++) {
        G[i].clear();
        vis[i] = 0;
    }
    return 0;
}
int main(){
    int T;scanf("%d",&T);
    while(T --) w33ha();
    return 0;
}

Training - INOI 2017
DP题

f [ i ] [ j ] f[i][j] f[i][j]表示一共升级了 i i i次,当前在 j j j号点去打道馆,获得的最多的经验。 g [ i ] [ j ] g[i][j] g[i][j]一共升级了 i i i次,当前在 j j j号点,继续升级,获得的最多经验。 s [ i ] s[i] s[i]是升级 i i i次之后的每次比赛的经验值, s e [ i ] se[i] se[i] E [ i ] E[i] E[i]的前缀和。

f [ i ] [ j ] = m a x ( g [ i − 1 ] [ k ] + s [ i ] ∗ ( s e [ j ] − s e [ k ] ) , f [ i ] [ j ] ) f[i][j] = max(g[i-1][k]+s[i]*(se[j] - se[k]),f[i][j]) f[i][j]=max(g[i1][k]+s[i](se[j]se[k]),f[i][j])
g [ i ] [ j ] = m a x ( g [ i − 1 ] [ k ] + s [ i ] ∗ ( s e [ j − 1 ] − s e [ k ] ) , g [ i ] [ j ] ) g[i][j] = max(g[i-1][k]+s[i]*(se[j-1] - se[k]),g[i][j]) g[i][j]=max(g[i1][k]+s[i](se[j1]se[k]),g[i][j])

对于每个 f [ i ] [ j ] f[i][j] f[i][j], g [ i ] [ j ] g[i][j] g[i][j]来说,其实 g [ i − 1 ] [ k ] − s [ i ] ∗ s e [ k ] g[i-1][k]-s[i]*se[k] g[i1][k]s[i]se[k]都可以用前缀最大值处理。然后在枚举j的时候计算 s [ i ] ∗ s e [ j ] s[i]*se[j] s[i]se[j] s [ i ] ∗ s e [ j − 1 ] s[i]*se[j-1] s[i]se[j1]即可。

/**
https://www.codechef.com/INOIPRAC/problems/TINOI17B
*/
#include
#define ll long long
using namespace std;
const ll INF = 1e16;
const int MAXN = 5e3 + 4;
ll cubify(ll x){
    ll v = 0;
    while(x){
        v += x % 10;
        x /= 10;
    }
    return v * v * v;
}
ll s[MAXN] , se[MAXN];
ll f[2][MAXN] , g[2][MAXN];
int n;
int main(){
    scanf("%d%lld",&n,&s[0]);
    for(int i=1;i<=n;i++){
        ll x;
        scanf("%lld",&x);
        se[i] = se[i-1] + x;
        s[i] = s[i-1] + cubify(s[i-1]);
    }
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    ll ans = 0;
    for(int i=0;i<=n;i++){
        int t = (i&1);
        memset(f[t],0,sizeof(f[t]));
        memset(g[t],0,sizeof(g[t]));
        ll maxn = - s[i] * se[i];
        for(int j=i+1;j<=n;j++){
            f[t][j] = maxn + s[i] * se[j];
            g[t][j] = maxn + s[i] * se[j-1];
            maxn = max(maxn , g[t^1][j] - s[i] * se[j]);
            ans = max(ans , f[t][j]);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

Brackets

区间dp,f[i][j]表示区间[i,j]的括号区间的最大和,直接硬做即可

/**
https://www.codechef.com/INOIPRAC/problems/INOI1602
*/
#include
#define ll long long
using namespace std;
const int MAXN = 704;
int K , n;
int v[MAXN] , b[MAXN];
ll f[MAXN][MAXN];
int w33ha(){
    memset(f,0,sizeof(f));
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int l=2;l<=n;l++){
        for(int i=1;i+l-1<=n;i++){
            int j = i+l-1;
            f[i][j] = max(f[i][j-1],f[i+1][j]);
            if(b[i]+K == b[j]){
                //cout << i << " " << j << endl;
                f[i][j] = max(v[i]+v[j]+f[i+1][j-1],f[i][j]);
            }
            for(int k=i+1;k<j;k++){
                f[i][j] = max(f[i][j] , f[i][k] + f[k+1][j]);
            }
        }
    }
    ll ans = 0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            ans = max(ans , f[i][j]);
    printf("%lld\n",ans);
    return 0;
}
/**
6 3
4 5 -2 1 1 6
1 3 4 2 5 6
*/
int main(){
    w33ha();
    return 0;
}

你可能感兴趣的:(随笔)