剩下还有些题以后再补……
1001
据说是从Kayles游戏拓展来的,这个游戏在网上都没啥资料……大概是这样的:
有一列石子,每次可以取走一个或连续的两个,每次取走后剩下的石子在原位置不动,不能操作者败。
这个游戏的特点就是SG打表之后存在规律,就是从某个 n n n 开始往后SG值存在循环节。
如果要验证循环节存在的话,假设循环开始的前一位为 a a a,循环节长度为 b b b,那么检验前 2 a + 2 b 2a+2b 2a+2b 位即可,如果都满足规律那么在这之后一定是循环的。具体证明可以手玩一下。
所以这题是一样的,打表找规律即可,规律可以参考官方题解。
代码就不贴了。
1002
n = 1 n=1 n=1 时显然不存在什么讨论空间。
n > 1 n>1 n>1 时,假如有 0,那么一定是贪心尽可能操作左边连续的0。假如有多的步数,每两步可以相互抵消,假如还多了一步,那么就让之前的随便一步多延伸一位,最后一步再操作一次那一位。
假如只有 1,那么直接开始抵消即可。但有一种情况特殊,如果只能操作一次,那么就不能抵消了,只能让最后的 1 变成 0。
代码如下:
#include <bits/stdc++.h>
using namespace std;
char s[200010];
int main()
{
int T;cin>>T;while(T--){
int n;long long k;
scanf("%d %lld",&n,&k);
scanf("%s",s+1);
if(n==1){
if(k&1)puts("0");
else puts("1");
continue;
}
int zero=0;
for(int i=1;i<=n;i++){
if(s[i]=='0'){
zero++;
if(k){
k--;s[i]='1';
int j=i;
while(j<n&&s[j+1]=='0')s[++j]='1';
i=j;
}
}
}
if(zero==0&&k==1ll)s[n]='0';
for(int i=1;i<=n;i++)
putchar(s[i]);puts("");
}
}
1004
考场上当然直接猜结论: 2 n − 1 − 1 2^{n-1}-1 2n−1−1,然后就过了。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define mod 998244353
int ksm(int x,int y){
int re=1;
for(;y;y>>=1){
if(y&1)re=1ll*re*x%mod;
x=1ll*x*x%mod;
}
return re;
}
int main()
{
int T;cin>>T;while(T--){
int n;cin>>n;
cout<<(ksm(2,n-1)-1+mod)%mod<<'\n';
}
}
证明的话,用数学归纳法。
设
f
(
n
)
f(n)
f(n) 为
n
n
n 个位置时的答案,假如现在是
n
+
1
n+1
n+1 个位置,那么第一次转移时考虑:先只用前
n
n
n 个位置,将
f
(
n
)
f(n)
f(n) 个物品转移出去,但是在第
f
(
n
)
f(n)
f(n) 个物品出去时,可以再将第
f
(
n
)
+
1
f(n)+1
f(n)+1 个物品放到
n
+
1
n+1
n+1 位置,然后再将第
f
(
n
)
f(n)
f(n) 个物品放到
n
+
1
n+1
n+1 位置,最后将剩下
f
(
n
)
−
1
f(n)-1
f(n)−1 个物品都放到
n
+
1
n+1
n+1 位置。于是这样转移出去了
f
(
n
)
+
1
f(n)+1
f(n)+1 个物品。
第二次转移则转移出去
f
(
n
−
1
)
+
1
f(n-1)+1
f(n−1)+1 个物品,第三次
f
(
n
−
2
)
+
1
f(n-2)+1
f(n−2)+1……最后一次
f
(
1
)
+
1
f(1)+1
f(1)+1。
( f ( 1 ) f(1) f(1) 本来没有定义,不妨设 f ( 1 ) = 0 f(1)=0 f(1)=0)
于是 f ( n + 1 ) = ∑ i = 1 n f ( i ) + 1 = ∑ i = 1 n 2 n − 1 = 2 n − 1 f(n+1)=\sum_{i=1}^n f(i)+1=\sum_{i=1}^n 2^{n-1}=2^n-1 f(n+1)=∑i=1nf(i)+1=∑i=1n2n−1=2n−1 □ \square □
1007
太抽象了,一开始想了半天要怎么做,最后实在没辙了bitset硬上然后就过了。
其实只需要看上下那两个单点有多少个同时相连的点,知道了这个之后组合数一下就能算出来chicken的身体部分的方案数。然后上面那两个鸡脚也是随便组合数一下就行。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define cn getchar
template<class TY>void read(TY &x){
x=0;int f1=1;char ch=cn();
while(ch<'0'||ch>'9'){if(ch=='-')f1=-1;ch=cn();}
while(ch>='0'&&ch<='9')x=x*10+(ch-'0'),ch=cn(); x*=f1;
}
template<class TY>void write2(TY x){
if(x>9)write2(x/10);
putchar(x%10+'0');
}
template<class TY>void write(TY x){
if(x<0)putchar('-'),x=-x;
write2(x);
}
bitset<1010> a[1010];
int du[1010];
long long C2(int x){
return x*(x-1)/2;
}
long long C4(int x){
return 1ll*x*(x-1)*(x-2)*(x-3)/24;
}
int main()
{
int T;cin>>T;while(T--){
int n,m;
read(n);read(m);
memset(du,0,sizeof(du));
for(int i=1;i<=m;i++){
int x,y;read(x);read(y);
a[x][y]=1;a[y][x]=1;
du[x]++;du[y]++;
}
long long ans=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
int c=(a[i]&a[j]).count();
if(c>=4){
ans+=C4(c)%mod*(C2(du[i]-4-a[i][j])+C2(du[j]-4-a[i][j]));
ans%=mod;
}
}
write(ans);puts("");
for(int i=1;i<=n;i++)
a[i].reset();
}
}
1009
输出有几段字母即可。
1010
首先条件可以转化成:两个隔着一位相邻的朋友之间距离不能超过 m m m。
设 f i , j f_{i,j} fi,j 表示最后一个选的朋友是 i i i,且距离上一个朋友至多为 j j j。那么枚举一个 k k k,用 f k , m − ( i − k ) f_{k,m-(i-k)} fk,m−(i−k) 更新 f i , i − k f_{i,i-k} fi,i−k,然后再枚举 j j j,用 f i , j f_{i,j} fi,j 更新 f i , j + 1 f_{i,j+1} fi,j+1 即可。利用了一点儿类似前缀和的性质。
还需要一个滚动数组。代码如下:
#include <bits/stdc++.h>
using namespace std;
int n,m,a[20010];
int f[2010][2010];
int main()
{
int Te;cin>>Te;while(Te--){
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];a[0]=a[n+1]=0;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
f[i][j]=(i==0?0:1e9);
for(int i=1;i<=n+1;i++){
for(int j=1;j<m;j++)f[i%m][j]=1e9;
for(int j=max(0,i-m+1);j<i;j++)
f[i%m][i-j]=a[i]+f[j%m][m-(i-j)];
for(int j=1;j<m-1;j++)
f[i%m][j+1]=min(f[i%m][j+1],f[i%m][j]);
}
cout<<f[(n+1)%m][m-1]<<'\n';
}
}
1011
当时脑瘫去考虑情况数除以总情况数了,其实直接考虑概率更好算,所以推式子的时候要多考虑考虑不同方向。
对于一个确定的 k k k,假设 n n n 在第 i i i 位,那么取到 n n n 的前提是前 i − 1 i-1 i−1 位中的最大值在前 k k k 位里,概率为 k i − 1 \dfrac k {i-1} i−1k,而 n n n 在第 i i i 位的概率为 1 n \dfrac 1 n n1。
所以有 ∑ i = k + 1 n k i − 1 × 1 n = k n ∑ i = k n − 1 1 i \sum_{i=k+1}^n\dfrac k {i-1}\times \dfrac 1 n=\dfrac k n\sum _{i=k}^{n-1} \dfrac 1 i ∑i=k+1ni−1k×n1=nk∑i=kn−1i1。
预处理一个前缀和则每个 k k k 就都可以 O ( 1 ) O(1) O(1) 求解了。
或者对 k k k 求导可以发现最优解是 n e \dfrac n e en,直接检查 ⌊ n e ⌋ \lfloor \dfrac n e \rfloor ⌊en⌋ 以及周围两个即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
double sum[10010];
double calc(int n,int k){
return 1.0*k/n*(sum[n-1]-sum[k-1]);
}
int main()
{
for(int i=1;i<=10000;i++)
sum[i]=sum[i-1]+1.0/i;
int T;cin>>T;while(T--){
int n;cin>>n;
if(n<=2){
cout<<"0\n";
continue;
}
int ans=0;
double val=calc(n,0);
int k=n/exp(1);
for(int i=max(k-1,1);i<=min(n,k+1);i++)
if(calc(n,i)>val)ans=i,val=calc(n,i);
cout<<ans<<'\n';
}
}
1012
硬币的交换其实很像网络流中一个流的移动,很容易想到用网络流来整。
由于操作存在先后顺序,所以需要按时间拆点跑网络流,每个点还需要拆开中间建边限制每个时刻的硬币数。
很多点都是废的,不需要拆 n × m n\times m n×m 个点,把需要的点建出来就行了。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define maxn 20010
#define inf 999999999
int n,m,k,lim[maxn],b[maxn];
int last[maxn],Id,S,T;
struct edge{int y,z,next;}e[maxn<<5];
int first[maxn],et;
void buildroad(int x,int y,int z){
e[++et]=(edge){y,z,first[x]};
first[x]=et;
}
void addedge(int x,int y,int z){
buildroad(x,y,z);buildroad(y,x,0);
}
int h[maxn],q[maxn],st,ed,cur[maxn];
bool bfs(){
memset(h,0,sizeof(h));
q[st=ed=1]=S;h[S]=1;
while(st<=ed){
int x=q[st++];cur[x]=first[x];
for(int i=first[x];i;i=e[i].next){
int y=e[i].y;
if(e[i].z&&!h[y]){
h[y]=h[x]+1;
q[++ed]=y;
}
}
}
return h[T]>0;
}
int dfs(int x,int flow){
if(x==T)return flow;int re=0;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y;cur[x]=first[x];
if(e[i].z&&h[y]==h[x]+1){
int p=dfs(y,min(e[i].z,flow-re));re+=p;
e[i].z-=p;e[i^1].z+=p;
if(re==flow)break;
}
}
if(!re)h[x]=0;
return re;
}
int main()
{
int Te;cin>>Te;while(Te--){
cin>>n>>m>>k;
for(int i=1;i<=Id;i++)first[i]=0;
for(int i=1;i<=n;i++)last[i]=0;
et=1;Id=2;S=1;T=2;
for(int i=1;i<=n;i++)cin>>lim[i];
for(int i=1;i<=n;i++)
addedge(S,last[i]=++Id,1);
for(int i=1;i<=m;i++){
int x,y;cin>>x>>y;
int X=++Id,Y=++Id,XX=++Id,YY=++Id;
addedge(last[x],Y,1);
addedge(last[y],X,1);
addedge(X,XX,lim[x]);
addedge(Y,YY,lim[y]);
addedge(last[x],X,inf);
addedge(last[y],Y,inf);
last[x]=XX;last[y]=YY;
}
for(int i=1;i<=k;i++){
int x;cin>>x;
addedge(last[x],T,lim[x]);
}
int ans=0;
while(bfs())ans+=dfs(S,inf);
cout<<ans<<'\n';
}
}