感觉是普及组模拟赛。。
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;
}
3 comments
感觉是普及组模拟赛。。
TQL %%%
ORz
orz