2021 10.19 模拟测试
\(\mathrm{T1}\)
\(\mathrm{Solution}\)
直接\(dp\) \(\mathrm{60pts}\)
正解和卡特兰数有关系。
假设远离家中为1,走进家中为0
其第一步一定为1,加下来的行动中,从前到后任意位置1的个数必然大于等于0的个数(不考虑第一个1),显然这类似卡特兰数
用折线发不难证明其方案数为\(\binom{n+m}{n}−\binom{n+m}{ n+1}\)
#include<bits/stdc++.h>
#define int long long
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=5e6+5;
const int mod=998244353;
int n,k,fac[N],inv[N],mi2[N],ans=0;
inline int dec(int a,int b){return a-b<0?(a-b+mod)%mod:a-b;}
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
inline int qpow(int a,int b)
{
int ret=1;
while(b)
{
if(b&1) ret=mul(ret,a);
a=mul(a,a);b>>=1;
}
return ret;
}
int C(int n,int m){return mul(fac[n],mul(inv[m],inv[n-m]));}
signed main()
{
// freopen("home.in","r",stdin);
// freopen("home.out","w",stdout);
n=in,k=in;
if(n>k){puts("0");return 0;;}
int tmp=mul(1,qpow(2,mod-2));
fac[0]=inv[0]=mi2[0]=1;
for(int i=1;i<=k;i++)
{
fac[i]=mul(fac[i-1],i);
mi2[i]=mul(mi2[i-1],tmp);
}
inv[k]=qpow(fac[k],mod-2);
for(int i=k-1;i;--i) inv[i]=mul(inv[i+1],i+1);
for(int i=n;i<=k;i+=2)
ans=add(ans,mul(dec(C(i-1,n-1+(i-n)/2),C(i-1,n-1+(i-n)/2+1)),mi2[i]));
write(ans);
}
\(\mathrm{T2}\)
\(\mathrm{Solution}\)
如果 \(\mathrm{k|n}\) ,那么把序列分成 k 组,每组\(\frac nk\)项
答案就是所有组最大项和最小项之差
构造方案如下:
每隔 k 个按照大小顺序放置一组内所有的元素。
答案显然同上
在这种情况下,显然将元素从小到大分组为最优答案
如果不是,任然把序列分为 k 组,有\(n\mod k\) 组为\(\frac nk+1\),其余为\(\frac nk\),
记\(dp[i][j]\)为选取了\(i\)个\(\frac nk+1\),\(j\)个\(\frac nk\)的最小答案
转移即可
#include<bits/stdc++.h>
#define int long long
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=1e6+5;
int n,k;
int a[N],dp[2][N];
signed main()
{
n=in,k=in;
for(int i=1;i<=n;i++) a[i]=in;
sort(a+1,a+n+1);
int q=n/k;
int r1=n%k,r2=k-r1;
int q1=q+1,q2=q;
for(int i=1;i<=r2;i++) dp[0][i]=a[i*q2]-a[q2*(i-1)+1]+dp[0][i-1];
for(int i=1;i<=r1;i++)
{
dp[i&1][0]=dp[i&1^1][0]+a[i*q1]-a[q1*(i-1)+1];
for(int j=1;j<=r2;++j)
{
int idx=i*q1+j*q2;
dp[i&1][j] = min( dp[i&1][j-1]+a[idx]-a[idx-q2+1] , dp[i&1^1][j] + a[idx]-a[idx-q1+1]);
}
}
write(dp[r1&1][r2]);
}
\(\mathrm{T3}\)
\(\mathrm{Solution}\)
我们首先需要对问题进行转化
发现直接求得答案较为困难,且其具有单调性,我们考虑二分答案
假设集合是静态的(没有插入操作)
我们当前的答案为mid,大于等于mid的元素有x个,小于mid的元素的和为sum
当且仅当满足\(sum\ge (c−x)∗mid\),存在至少操作mid次的方案
证明如下:
原问题等价于向mid个集合中每个填充c个元素
每个集合中不能出现重复元素
第i个元素可以填充\(a_i\)次
显然每个元素最多使用\(min(mid,a_i)\)次
对于大于等于\(mid\)的元素,其可使用\(mid\)次,总计\(x∗mid\)
对于小于\(mid\)的元素,其可使用\(\sum a_i\)次,总计\(sum\)
证毕
当集合是动态时
我们需要一种数据结构维护大于等于某个元素的元素个数和小于等于其的元素的和
因为插入操作仅修改一个元素
且善良的出题人没有强制在线
树状数组即可维护这些信息
#include<bits/stdc++.h>
#define int long long
#define in read()
using namespace std;
inline int read()
{
int data=0;int w=1; char ch=0;
ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=1e6+5;
int n,tot,cnt,num,b[N];
struct node{
int opt,x;
}a[N];
struct tree{
int t1[N],t2[N];
inline int lowbit(int x){return x&(-x);}
inline void update(int k,int val){for(;k<=cnt;k+=lowbit(k)) t1[k]++,t2[k]+=val;}
inline int query(int x)
{
if(x>num) return 0;
int sum=0,mid=0,cur=0;
for(int i=20;i>=0;--i)
{
if(cur+(1<<i)>cnt) continue;
if((x-(num-sum-t1[cur+(1<<i)]))*b[cur+(1<<i)]<=mid+t2[cur+(1<<i)])
{
cur+=(1<<i);
sum+=t1[cur],mid+=t2[cur];
}
}
return mid/(x+sum-num);
}
}t;
signed main()
{
// freopen("set3.in","r",stdin);
// freopen("soc.out","w",stdout);
n=in;
for(int i=1;i<=n;i++) a[i].opt=in,a[i].x=in;
for(int i=1;i<=n;i++) if(a[i].opt==1) b[++tot]=a[i].x;
sort(b+1,b+tot+1);
cnt=unique(b+1,b+tot+1)-b-1;
for(int i=1;i<=n;i++) if(a[i].opt==1) a[i].x=lower_bound(b+1,b+cnt+1,a[i].x)-b;
for(int i=1;i<=n;i++)
{
if(a[i].opt==1) t.update(a[i].x,b[a[i].x]),num++;
else write(t.query(a[i].x)),puts("");
}
}
\(\mathrm{T4}\)
\(\mathrm{Solution}\)
显然这道题可以分为两个部分
第一步,对于每一支军队,确定其能获得最大的利益
第二步,确定军队在约束条件下获得的最大利益和
我们先考虑第一步
显然可以使用弗洛伊德算法求出任意两点间的距离
由于城堡可以被攻破多次,显然对于处于同一地点的城堡,如果其收益小于等于其他城堡且防御力大于等于该城堡,则其一定不会被选中
如此我们便可以使得一个地点的所有城堡按照防御力和收益同时递减的方式排列
同样地,我们令一个地点的军队按照攻击力递减排列
不难发现,对于处于同一地点的军队,他们所匹配的城堡一定是单调的
所以对于处于同一地点的军队,我们最多访问所有军队1次
对于每个军队,我们最多访问每个地点一次
所以总时间复杂度为O(ns)
对于第二步
我们先把答案设置为所有收益为正的军队的收益和
我们把所有含有依赖关系的军队分为两组,第一组的所有军队收益为正
第二组的所有军队收益为负
对于第一组另原点向其链接容量为收益的边
对于第二组另原点向其链接容量为收益相反数的边
对于所有依赖关系,依赖军队向其被依赖军队链接容量无穷的边
减去当前图的最小割即为最终答案
#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=2e5+10;
const int inf=1e17;
int n,m,s,b,t,ans,tmp;
int dis[110][110],use[N],f[N];
int first[N],cnt=1,S,T,dep[N],cur[N];
struct army{int a,f,p,id,val;bool flag;};
struct castle{int d,g;};
struct edge{int v,flow,nxt;}e[N<<1];
vector<army>ar[110];
vector<castle>cs[110],css[110];
inline void add(int u,int v,int w){e[++cnt]=(edge){v,w,first[u]};first[u]=cnt;}
bool cmp1(army a,army b){return a.a<b.a;}
bool cmp2(castle a,castle b){return a.d==b.d?a.g<b.g:a.d<b.d;}
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
bool bfs()
{
queue<int>q;
for(int i=0;i<=s+1;i++) dep[i]=-1;
q.push(S);dep[S]=1;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=first[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(dep[v]==-1&&e[i].flow)
{
dep[v]=dep[u]+1;
if(v==T) return 1;
cur[v]=first[v];
q.push(v);
}
}
}
return 0;
}
int dinic(int u,int ex)
{
if(u==T) return ex;
int flow=0;
for(int &i=cur[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(e[i].flow&&dep[v]==dep[u]+1)
{
int k=dinic(v,min(ex-flow,e[i].flow));
if(k)
{
e[i].flow-=k;
e[i^1].flow+=k;
flow+=k;
if(flow==ex) return ex;
}
}
}
dep[u]=0;
return flow;
}
signed main()
{
n=in,m=in;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)dis[i][j]=inf;
for(int i=1;i<=m;i++)
{
int u=in,v=in;
dis[u][v]=dis[v][u]=1;
}
for(int i=1;i<=n;i++) dis[i][i]=0;
floyd();
s=in,b=in,t=in;
for(int i=1;i<=s;i++)
{
int x=in,a=in,f=in,p=in;
ar[x].pb((army){a,f,p,i,0,0});
}
for(int i=1;i<=b;i++)
{
int x=in,d=in,g=in;
cs[x].pb((castle){d,g});
}
for(int i=1;i<=n;i++)sort(ar[i].begin(),ar[i].end(),cmp1);
for(int i=1;i<=n;i++)sort(cs[i].begin(),cs[i].end(),cmp2);
for(int i=1;i<=n;i++)
for(int j=0;j<cs[i].size();j++)
{
while(j<cs[i].size()&&css[i].size()&&cs[i][j].g<css[i][css[i].size()-1].g) j++;
if(j>=cs[i].size()) break;
css[i].pb(cs[i][j]);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(!css[j].size()) continue;
int l=-1;
for(int k=0;k<ar[i].size();k++)
{
while(l+1<css[j].size()&&css[j][l+1].d<=ar[i][k].a) l++;
if(l!=-1&&ar[i][k].f>=dis[i][j])
ar[i][k].flag=1,ar[i][k].val=max(ar[i][k].val,css[j][l].g);
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<ar[i].size();j++)
{
use[ar[i][j].id]=ar[i][j].flag;
if(use[ar[i][j].id]) f[ar[i][j].id]=ar[i][j].val-ar[i][j].p;
else f[ar[i][j].id]=-inf;
ans+=max((long long)0,f[ar[i][j].id]);
}
int S=0,T=s+1;
for(int i=1;i<=t;i++)
{
int u=in,v=in;
add(u,v,inf);add(v,u,0);
}
for(int i=1;i<=s;i++)
{
if(f[i]>0) add(S,i,f[i]),add(i,S,0);
else if(f[i]<0) add(i,T,-f[i]),add(T,i,0);
}
while(bfs()) while(tmp=dinic(S,inf)) ans-=tmp;
write(ans);
}

浙公网安备 33010602011771号