省选杂题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 了。

posted @ 2023-02-09 17:35  gtm1514  阅读(22)  评论(0编辑  收藏  举报