感觉是普及组模拟赛。。

A

手玩,发现 $n =2$ 很特殊:因为谁先手谁就能赢。

读入这么大,肯定之和数的性质有关,由于这是 NOIP 模拟赛,猜一发奇偶性。

当 $n$ 为偶数时:黑妞必胜。(等价于有最大的牌的人必胜)

黑妞后手时:先手出 $i$ 他跟 $i+1$,一直到对手只剩一张牌的时候出最大的牌,然后就可以跑了。(即使中间用了最大的牌也没关系:因为这样等价于化为了 $n-2$ 的子问题)

黑妞先手时:自己先出个最小的,然后按照上述策略即可。在自己剩下两张牌的时候突然打大牌。

当 $n$ 为奇数时:先手必胜

先手为皮蛋时:第一次出 $1$,转化为了 $n$ 为偶数并且皮蛋有最大牌的必胜局面。

先手为黑妞时:总有策略让皮蛋的 $1$ 一直留在手里,皮蛋出最大牌之后会转化为 $n$ 为偶数的有最大牌的局面。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e5 + 5;
char str[MAXN];

int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%s",str+1);int len = strlen(str+1);
        int o;scanf("%d",&o);
        int x = str[len]-'0';
        printf("%d\n",o|((!(len==1&&x<=3))&(!(x&1))));
    }
    return 0;
}

B

染色问题。倒序处理,假设当前染行,只需要知道有多少列被染了就行。

模拟即可。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

inline char nc(){
    #define SIZE 1000000+3
    static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
    if(p1 == p2){
        p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
        if(p1 == p2) return -1;
    }
    return *p1++;
    #undef SIZE
}

template <typename T>
inline void read(T &x){
    x = 0;int flag = 0;char ch = nc();
    while(!isdigit(ch)){
        if(ch == '-') flag = 1;
        ch = nc();
    }
    while(isdigit(ch)){
        x = (x<<1) + (x<<3) + (ch^'0');
        ch = nc();
    }
    if(flag) x = -x;
}

const int MAXN = 1e6 + 5;
int n,m,k;
int x[MAXN],y[MAXN],z[MAXN];
bool vis[2][MAXN];

int main(){
    read(n);read(m);read(k);
    FOR(i,1,k) read(x[i]),read(y[i]),read(z[i]);
    int row=0,col=0;LL ans = 0;
    ROF(i,k,1){
        if(vis[x[i]][y[i]]) continue;
        vis[x[i]][y[i]] = 1;
        if(x[i] == 0) ans += z[i]*std::max(0,m-col),++row;
        else ans += z[i]*std::max(0,n-row),++col;
    }
    printf("%lld\n",ans);
    return 0;
}

C

暴力直接 nth_element

一个靠谱的做法是求出所有交点,只有在交点才会变换排名。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 7000+5;
int n,q;
int now;

struct Node{
    int k,b,id;
    Node(int k=0,int b=0,int id=0) : k(k),b(b),id(id) {}
}a[MAXN];

inline bool cmp(const Node &a,const Node &b){
    LL ay = 1ll*a.k*now+a.b,by = 1ll*b.k*now+b.b;
    return ay==by ? a.id < b.id : ay > by;
}

int main(){
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d%d",&a[i].k,&a[i].b),a[i].id = i;
    scanf("%d",&q);
    FOR(i,1,q){
        int x;scanf("%d%d",&now,&x);
        std::nth_element(a+1,a+x,a+n+1,cmp);
        printf("%d\n",a[x].id);
    }
    return 0;
}

D

可以证明每次划分的一定至少有一个部分大小为 $1$(这样 $1$ 减的尽量多)

设 $f_{i,j}$ 表示分别还剩 $i,j$ 个的答案,$O(n^3)$ 。

写出转移式子,是个前缀 $\min$ 的形式,树状数组维护即可。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 2000+5;
LL f[MAXN][MAXN],sma[MAXN],smb[MAXN];
int n,m,a[MAXN],b[MAXN];

inline void upmin(LL &x,LL y){
    if(x > y) x = y;
}

struct BIT{
    #define lowbit(x) ((x)&(-(x)))
    LL tree[MAXN];

    inline void add(int pos,LL x){
        ++pos;
        while(pos < MAXN){
            tree[pos] = std::min(tree[pos],x);
            pos += lowbit(pos);
        }
    }

    inline LL qry(int pos){
        LL res = 1e18;++pos;
        while(pos){
            res = std::min(res,tree[pos]);
            pos -= lowbit(pos);
        }
        return res;
    }
}mn1[MAXN],mn2[MAXN];

int main(){
    freopen("D.in","r",stdin);
    freopen("D.out","w",stdout);
    scanf("%d%d",&n,&m);
    FOR(i,1,n) scanf("%d",a+i),sma[i] = sma[i-1]+a[i];
    FOR(i,1,m) scanf("%d",b+i),smb[i] = smb[i-1]+b[i];
    FOR(i,1,m) smb[i] -= i;
    FOR(i,1,n) sma[i] -= i;
    FOR(i,1,n) a[i]--;FOR(i,1,m) b[i]--;
    CLR(f,0x3f);f[0][0] = 0;
    CLR(mn1,0x3f);CLR(mn2,0x3f);
    mn1[0].add(0,f[0][0]+sma[0]*b[1]);
    mn2[0].add(0,f[0][0]+smb[0]*a[1]);
    FOR(i,1,n){
        FOR(j,1,m){
            upmin(f[i][j],mn1[j-1].qry(i-1)+sma[i]*b[j]);
            upmin(f[i][j],mn2[i-1].qry(j-1)+smb[j]*a[i]);
            mn1[j].add(i,f[i][j]-sma[i]*b[j+1]);
            mn2[i].add(j,f[i][j]-smb[j]*a[i+1]);
        }
    }
    printf("%lld\n",f[n][m]);
    return 0;
}
Last modification:October 1st, 2020 at 07:17 pm
如果觉得我的文章对你有用,请随意赞赏