省选杂题1
来刷历年联考。
[六省联考 2017] 期末考试
一眼题。
首先猜一下都知道这个最后的时间可以二分,那二分一波然后每次线性计算贡献就行了。
交了第一发发现 45 分。调了一下又交了一发 90 分。查看讨论区发现要特判 \(C=10^{16}\)。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
int A,B,C,n,m,t[200010],b[200010];
int check(int mid){
int ans=0;
if(A>=B){
for(int i=1;i<=m;i++){
if(b[i]>mid)ans+=(b[i]-mid)*B;
}
}
else{
int d=0,ret=0;
for(int i=1;i<=m;i++){
if(b[i]<mid)d+=mid-b[i];
else ret+=b[i]-mid;
}
if(d>=ret)ans+=ret*A;
else ans+=d*A+(ret-d)*B;
}
for(int i=1;i<=n;i++){
if(t[i]<mid)ans+=(mid-t[i])*C;
}
return ans;
}
signed main(){
scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&n,&m);
int mx=0;
for(int i=1;i<=n;i++)scanf("%lld",&t[i]);
for(int i=1;i<=m;i++)scanf("%lld",&b[i]);
sort(b+1,b+m+1);sort(t+1,t+n+1);
if(C==1e16){
printf("%lld\n",check(t[1]));
return 0;
}
int l=1,r=200000;
while(l<r){
int mid=(l+r)>>1;
if(check(mid)>check(mid+1))l=mid+1;
else r=mid;
}
printf("%lld\n",check(l));
return 0;
}
[六省联考 2017] 相逢是问候
一开始没读清题以为 \(c\) 是变的,感觉好不可做啊。后来发现 \(c\) 不变那没事了。
考虑扩展欧拉定理那个东西,发现每个位置只有前 \(O(\log n)\) 次修改是有用的。于是势能线段树,并且暴力。
然而就很不想写。没写。据说还要套个光速幂要不然卡常。那更不想写了。
[六省联考 2017] 组合数问题
当初 joke3579 给我看这个东西发现答案就是
\[[x^r](1+x)^{nk}\bmod (x^k-1)
\]
的时候很震撼。现在回来看看发现一个虽然复杂度多一个 \(k\) 但是比较不动脑子的方法。
套路单位根反演,答案是
\[\frac 1k\sum_{i=0}^{k-1}w_k^{-ir}(w_k^i+1)^{nk}
\]
把 \(k\) 次单位根当做循环卷积卷一下就能算了。复杂度 \(O(k^3\log nk)\),然而由于模数乘 \(k\) 以后乘起来爆 long long 调了半天。感觉不如上边那个。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
using namespace std;
int n,mod,k,r,ans[60],a[60],las[60];
const long double pi=acos(-1);
void qpow(int a[],int b,int ans[]){
static int tmp[60];
while(b){
if(b&1){
for(int i=0;i<k;i++){
for(int j=0;j<k;j++){
tmp[(i+j)%k]=(tmp[(i+j)%k]+(__int128)ans[i]*a[j])%mod;
}
}
for(int i=0;i<k;i++)ans[i]=tmp[i],tmp[i]=0;
}
for(int i=0;i<k;i++){
for(int j=0;j<k;j++){
tmp[(i+j)%k]=(tmp[(i+j)%k]+(__int128)a[i]*a[j])%mod;
}
}
for(int i=0;i<k;i++)a[i]=tmp[i],tmp[i]=0;
b>>=1;
}
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&mod,&k,&r);mod*=k;
for(int i=0;i<k;i++){
for(int j=0;j<k;j++)a[j]=ans[j]=0;
ans[i*(k-r)%k]=1;a[i]++;a[0]++;
qpow(a,1ll*n*k,ans);
for(int j=0;j<k;j++)las[j]=(las[j]+ans[j])%mod;
}
long double ret=0;
for(int i=0;i<k;i++)ret+=cosl(2*pi*i/k)*las[i];
int tmp=ret+0.5;
if(tmp<0)tmp=(mod-((-tmp)%mod))%mod;
printf("%lld\n",tmp/k);
return 0;
}
D1 感觉:如果放到现在真的能阿克一车,可惜现在是六年后。
[六省联考 2017] 摧毁“树状图”
六年前的联考题居然有这么 ex 的分讨。
题目说人话就是找两条边不重合的路径删掉使得剩下的连通块最多。那显然树形 dp。
然后咕。不会。
[六省联考 2017] 分手是祝愿
水题。
设 \(dp_i\) 是最优 \(i\) 步减少一步的期望步数,那么显然
\[dp_i=1+\frac{n-i}n(dp_{i+1}+dp_i)
\]
\[dp_i=\frac{n+(n-i)dp_{i+1}}i
\]
暴力求初始最优步数,然后 dp 初值为 \(dp_n=1\)。算就行了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int mod=100003;
int n,k,ans,cnt,dp[100010],a[100010];
int qpow(int a,int b){
int ans=1ll;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1ll;
}
return ans;
}
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=n;i>=1;i--){
if(a[i]){
cnt++;
for(int j=1;j*j<=i;j++){
if(i%j==0){
a[j]^=1;
if(j*j!=i)a[i/j]^=1;
}
}
}
}
if(cnt<=k)ans=cnt;
else{
dp[n+1]=0;
for(int i=n;i>=1;i--){
dp[i]=(n-i)*dp[i+1]%mod;
dp[i]=(dp[i]+n)%mod*qpow(i,mod-2)%mod;
}
for(int i=cnt;i>k;i--)ans=(ans+dp[i])%mod;
ans=(ans+k)%mod;
}
for(int i=1;i<=n;i++)ans=ans*i%mod;
printf("%lld",ans);
return 0;
}
[六省联考 2017] 寿司餐厅
感觉不少流题的第一个难点在于想到这是个流题。
然后就比较好搞了。正权源点负权汇点,每个区间向左右端点缩小 \(1\) 的区间连边,每个单个寿司向编号连边,然后算一下价值连上就行了。经典最大权闭合子图。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
const int inf=0x3f3f3f3f;
struct node{
int v,w,next;
}edge[5000010];
int n,m,ans,S,T,tot=1,head[11010],dis[11010],head2[11010];
void add(int u,int v,int w){
edge[++tot].v=v;edge[tot].w=w;edge[tot].next=head[u];head[u]=tot;
}
queue<int>q;
bool bfs(int st){
for(int i=0;i<=T;i++)head2[i]=head[i],dis[i]=0;
q.push(st);dis[st]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=edge[i].next){
if(edge[i].w&&!dis[edge[i].v]){
dis[edge[i].v]=dis[x]+1;
if(edge[i].v==T){
while(!q.empty())q.pop();
return true;
}
q.push(edge[i].v);
}
}
}
return false;
}
int dfs(int x,int flow){
if(x==T)return flow;
int sum=0;
for(int &i=head2[x];i;i=edge[i].next){
if(edge[i].w&&dis[edge[i].v]==dis[x]+1){
int ret=dfs(edge[i].v,min(flow,edge[i].w));
if(ret){
edge[i].w-=ret;edge[i^1].w+=ret;
flow-=ret;sum+=ret;
if(!flow)break;
}
else dis[edge[i].v]=-1;
}
}
return sum;
}
int a[110],id[110][110];
bool used[11010];
int main(){
scanf("%d%d",&n,&m);T=n*n+1000+1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(used[a[i]])continue;
used[a[i]]=true;
add(a[i],T,m*a[i]*a[i]);
add(T,a[i],0);
}
int num=1000;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
id[i][j]=++num;
}
}
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
if(x>=0){
add(S,id[i][i],x);add(id[i][i],S,0);
ans+=x;
}
else{
add(id[i][i],T,-x);add(T,id[i][i],0);
}
add(id[i][i],T,a[i]);add(T,id[i][i],0);
add(id[i][i],a[i],inf);add(a[i],id[i][i],0);
for(int j=i+1;j<=n;j++){
scanf("%d",&x);
if(x>=0){
add(S,id[i][j],x);add(id[i][j],S,0);
ans+=x;
}
else{
add(id[i][j],T,-x);add(T,id[i][j],0);
}
add(id[i][j],id[i+1][j],inf);add(id[i+1][j],id[i][j],0);
add(id[i][j],id[i][j-1],inf);add(id[i][j-1],id[i][j],0);
}
}
while(bfs(S))ans-=dfs(S,inf);
printf("%d\n",ans);
return 0;
}
D2 感觉:这辈子感觉都会不了 dp 了。
快踩