正题
A - Range Product
考虑一下正负就可以了。
#include<bits/stdc++.h>
using namespace std;
int a,b;
int main(){
scanf("%d %d",&a,&b);
if(a<=0 && b>=0) printf("Zero");
else if(a>0) printf("Positive");
else printf(((b-a+1)&1)?"Negative":"Positive");
}
B - Box and Ball
维护一下当前每一个地方是否可能有红球,以及有多少个球,然后转移的时候,球的个数可以直接转移,是否有红球就或一下,如果拿走之后不剩球了,那么这个地方也改成不可能有红球。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
bool tf[N];
int a[N],n,m;
int main(){
scanf("%d %d",&n,&m);tf[1]=true;
for(int i=1;i<=n;i++) a[i]=1;
int x,y;
for(int i=1;i<=m;i++){
scanf("%d %d",&x,&y);
a[x]--;a[y]++;
if(tf[x]) tf[y]=true;
if(!a[x]) tf[x]=false;
}
int ans=0;
for(int i=1;i<=n;i++) ans+=tf[i];
printf("%d\n",ans);
}
C - Knot Puzzle
考虑有解的充要条件是存在相邻两个相加 ≥ k \geq k ≥k,考虑反证法即可证明,构造方案也十分简单,从两边减就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n,m;
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int pos=0;
for(int i=1;i<n;i++) if(a[i]+a[i+1]>=m) {pos=i;break;}
if(!pos) printf("Impossible");
else{
printf("Possible\n");
for(int i=1;i<pos;i++) printf("%d\n",i);
for(int i=n-1;i>pos;i--) printf("%d\n",i);
printf("%d\n",pos);
}
}
D - Stamp Rally
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct node{
int x,y,z,ans;
}w[N];
pair<int,int> p[N];
int n,m,q,fa[N],sz[N];
int findpa(int x){return fa[x]!=x?findpa(fa[x]):x;}
void solve(vector<int>&A,int l,int r){
if(A.size()==0) return ;
if(l==r){
for(int i=0;i<A.size();i++) w[A[i]].ans=l;
return ;
}
vector<int> L,R;
vector<pair<int,int> > op;
int mid=(l+r)/2;
for(int i=l;i<=mid;i++) {
int fx=findpa(p[i].first),fy=findpa(p[i].second);
if(fx!=fy){
if(sz[fx]>sz[fy]) swap(fx,fy);
op.push_back(make_pair(fx,fy));
fa[fx]=fy;sz[fy]+=sz[fx];
}
}
for(int i=0;i<A.size();i++){
int fx=findpa(w[A[i]].x),fy=findpa(w[A[i]].y);
bool we;
if(fx==fy) we=(sz[fx]>=w[A[i]].z);
else we=(sz[fx]+sz[fy]>=w[A[i]].z);
if(we) L.push_back(A[i]);
else R.push_back(A[i]);
}
solve(R,mid+1,r);
for(int i=op.size()-1;i>=0;i--) fa[op[i].first]=op[i].first,sz[op[i].second]-=sz[op[i].first];
solve(L,l,mid);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) sz[i]=1,fa[i]=i;
for(int i=1;i<=m;i++) scanf("%d %d",&p[i].first,&p[i].second);
scanf("%d",&q);
vector<int> A;
for(int i=1;i<=q;i++) scanf("%d %d %d",&w[i].x,&w[i].y,&w[i].z),A.push_back(i);
solve(A,1,m);
for(int i=1;i<=q;i++) printf("%d\n",w[i].ans);
}
D - Stamp Rally
发现答案是可二分的,考虑整体二分,只需要使用带撤销的并查集来维护就好了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct node{
int x,y,z,ans;
}w[N];
pair<int,int> p[N];
int n,m,q,fa[N],sz[N];
int findpa(int x){return fa[x]!=x?findpa(fa[x]):x;}
void solve(vector<int>&A,int l,int r){
if(A.size()==0) return ;
if(l==r){
for(int i=0;i<A.size();i++) w[A[i]].ans=l;
return ;
}
vector<int> L,R;
vector<pair<int,int> > op;
int mid=(l+r)/2;
for(int i=l;i<=mid;i++) {
int fx=findpa(p[i].first),fy=findpa(p[i].second);
if(fx!=fy){
if(sz[fx]>sz[fy]) swap(fx,fy);
op.push_back(make_pair(fx,fy));
fa[fx]=fy;sz[fy]+=sz[fx];
}
}
for(int i=0;i<A.size();i++){
int fx=findpa(w[A[i]].x),fy=findpa(w[A[i]].y);
bool we;
if(fx==fy) we=(sz[fx]>=w[A[i]].z);
else we=(sz[fx]+sz[fy]>=w[A[i]].z);
if(we) L.push_back(A[i]);
else R.push_back(A[i]);
}
solve(R,mid+1,r);
for(int i=op.size()-1;i>=0;i--) fa[op[i].first]=op[i].first,sz[op[i].second]-=sz[op[i].first];
solve(L,l,mid);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) sz[i]=1,fa[i]=i;
for(int i=1;i<=m;i++) scanf("%d %d",&p[i].first,&p[i].second);
scanf("%d",&q);
vector<int> A;
for(int i=1;i<=q;i++) scanf("%d %d %d",&w[i].x,&w[i].y,&w[i].z),A.push_back(i);
solve(A,1,m);
for(int i=1;i<=q;i++) printf("%d\n",w[i].ans);
}
E - Candy Piles
考虑排序后把它看成一堆方块叠起来,第
i
i
i列有
a
i
a_i
ai个方块。
两种操作可以看做每次看做删掉最后一行或者最后一列,谁操作最后一步谁就输了,转化问题为当前有个棋子在右下角,每次可以向左或者向上移一步,谁移动了最后一步谁就获胜。
一个点是必胜点当且仅当走一步可以走到至少一个必败点,一个点是必败点当且仅当走一步走到的都是必胜点。
若
(
i
,
j
)
(i,j)
(i,j)为必败点,那么
(
i
−
1
,
j
+
1
)
(i-1,j+1)
(i−1,j+1)为必败点,这个显然,因为
(
i
−
1
,
j
)
,
(
i
,
j
+
1
)
(i-1,j),(i,j+1)
(i−1,j),(i,j+1)都是必胜点。
若
(
i
,
j
)
(i,j)
(i,j)为必胜点,那么
(
i
−
1
,
j
+
1
)
(i-1,j+1)
(i−1,j+1)为必胜点,考虑从左到右,从上到下归纳就可以了,
(
i
,
j
)
(i,j)
(i,j)是必胜点,说明
(
i
+
1
,
j
)
(i+1,j)
(i+1,j)和
(
i
,
j
−
1
)
(i,j-1)
(i,j−1)至少有一个必败点,所以
(
i
,
j
+
1
)
(i,j+1)
(i,j+1)和
(
i
−
1
,
j
)
(i-1,j)
(i−1,j)至少有一个必败点,从而推出
(
i
−
1
,
j
+
1
)
(i-1,j+1)
(i−1,j+1)为必胜点。
那么我们只需要不断的往左上走,这样可以使得我们的NP值不变,最后在考虑该点左边点和上面点的NP值,这个NP值显然只跟格子的奇偶性有关,然后就可以得出该点的NP值了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int now=n;
while(now && n-now+2<=a[now-1]) now--;
bool A=true,B=(((a[now]-(n-now+1))&1)^1);
a[now]=n-now+1;
for(int i=now-1;i>=1;i--) if(a[i]==a[i+1]) A^=true;
else break;
A&=B;
printf(A?"Second":"First");
}
F - Leftmost Ball
对于一个合法序列,考虑一种
1
1
1到
n
n
n的一种置换,出来肯定也是合法的。
设第一个
i
i
i出现的位置是
p
i
p_i
pi,第
i
i
i个
0
0
0出现的位置是
q
i
q_i
qi。
那么显然有:
p
i
<
p
i
+
1
,
q
i
<
p
i
,
q
i
<
q
i
+
1
p_i<p_{i+1},q_i<p_i,q_i<q_{i+1}
pi<pi+1,qi<pi,qi<qi+1
考虑设置状态
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示当前还剩下
i
i
i个0和后
j
j
j个数的方案数。
有
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
]
[
j
−
1
]
∗
C
i
+
j
∗
(
k
−
1
)
−
1
k
−
2
f[i][j]=f[i-1][j]+f[i][j-1]*C_{i+j*(k-1)-1}^{k-2}
f[i][j]=f[i−1][j]+f[i][j−1]∗Ci+j∗(k−1)−1k−2
前面是考虑再放一个
0
0
0,后面是考虑先放一个
n
−
j
+
1
n-j+1
n−j+1,后面再按组合数分配一下位置(因为后面这些数就没有位置限制了)。
这个过程需要保证
i
≤
j
i\leq j
i≤j,那么就做完了。
#include<bits/stdc++.h>
using namespace std;
const int N=2010,mod=1000000007;
int f[N][N],fac[4000010],inv[4000010],n,k;
void ad(int&x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
int ksm(int x,int t){
int tot=1;
while(t){
if(t&1) tot=1ll*tot*x%mod;
x=1ll*x*x%mod;
t/=2;
}
return tot;
}
int C(int x,int y){return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
int main(){
scanf("%d %d",&n,&k);
if(n==1 || k==1) {printf("1\n");return 0;}
fac[0]=1;for(int i=1;i<=4000000;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[4000000]=ksm(fac[4000000],mod-2);for(int i=3999999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
f[0][0]=1;
for(int i=0;i<=n;i++)
for(int j=i;j<=n;j++){
if(i && j>=i-1) ad(f[i][j],f[i-1][j]);
if(j) f[i][j]=(f[i][j]+1ll*f[i][j-1]*C(i+(k-1)*j-1,k-2))%mod;
}
printf("%d\n",1ll*f[n][n]*fac[n]%mod);
}