Loading

11.8 正睿做题笔记

T1

感觉还是比较水的一道题,我们直接用 bitset 硬做就可以了。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 20100
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

bitset<20100> bit[N],b0,b1,b2,b3;
char s[N];
int n,m;

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);
    for(int i=1;i<=m;i++){
        b0.reset();b1.reset();b2.reset();b3.reset();
        scanf("%s",s+1);
        for(int j=1;j<=n;j++){
            if(s[j]=='0') b0[j]=1;
            else if(s[j]=='1') b1[j]=1;
            else if(s[j]=='2') b2[j]=1;
            else if(s[j]=='3') b3[j]=1;
        }
        for(int j=1;j<=n;j++){
            if(s[j]=='0') continue;
            else if(s[j]=='1') bit[j]=bit[j]^(b2|b3);
            else if(s[j]=='2') bit[j]=bit[j]^(b1|b3);
            else if(s[j]=='3') bit[j]=bit[j]^(b1|b2|b3);
            bit[j][j]=0;
        }
    }
    int Ans=0;
    for(int j=1;j<=n;j++){
        Ans+=bit[j].count();
    }
    assert(Ans%2==0);
    Ans/=2;
    printf("%d\n",Ans);
    return 0;
}

T2

这个题本质是 dp 优化,首先我们考虑二分如何 check,我们考虑设 dp \(f_i\) 表示前 \(i\) 个值,在每段都大于等于 \(0\) 的情况下,最多能分成多少段。朴素 dp 是 \(n^2\) 的,我们考虑整个转移可以用 bit 优化。

代码:

#include<bits/stdc++.h>
#include<iostream>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const dd eps=1e-4;
const dd eps2=1e-10;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Max(T a,T b){return a<b?b:a;}

int n,a[N],k;

struct BIT{
    int p[N];
    inline void Init(int n){
        for(int i=1;i<=n;i++) p[i]=-1;
    }
    inline int lowbit(int x){return x&(-x);}
    inline void Add(int w,int x){
        for(int i=w;i<=n;i+=lowbit(i)) p[i]=Max(x,p[i]);
    }
    inline int GetMax(int w){
        int res=-INF;
        for(int i=w;i;i-=lowbit(i)) res=Max(res,p[i]);return res;
    }
}bit;

inline void Init(){
    read(n);read(k);
    for(int i=1;i<=n;i++) read(a[i]);
}

int f[N],d[N];
dd b[N],c[N],rk[N];

inline bool Check(dd mid){
    bit.Init(n);
    // printf("mid=%lf\n",mid);
    for(int i=1;i<=n;i++) b[i]=1.0*a[i]-mid;
    // printf("b:");for(int i=1;i<=n;i++) printf("%lf ",b[i]);puts("");
    for(int i=1;i<=n;i++) b[i]+=b[i-1];   
    for(int i=1;i<=n;i++) c[i]=b[i];
    sort(c+1,c+n+1);int len=unique(c+1,c+n+1)-c-1;
    // printf("c:");for(int i=1;i<=n;i++) printf("%lf ",b[i]);puts("");
    for(int i=1;i<=n;i++){
        int Rank=lower_bound(c+1,c+len+1,b[i])-c;
        rk[Rank]=b[i];d[i]=Rank;
    }
    // printf("d:");for(int i=1;i<=n;i++) printf("%d ",d[i]);puts("");
    for(int i=1;i<=n;i++){
        if(b[i]>=0) f[i]=1;
        else{f[i]=-1;continue;}
        int Ans=bit.GetMax(d[i]);
        f[i]=Max(f[i],Ans+1);
        bit.Add(d[i],f[i]);
    }
    // printf("f:");for(int i=1;i<=n;i++) printf("%d ",f[i]);puts("");
    return f[n]>=k;
}

inline void Solve(){
    dd l=1,r=100;
    while(r-l>eps){
        // printf("%lf\n",l);
        dd mid=(l+r)/2;
        if(Check(mid)) l=mid;
        else r=mid;
    }
    printf("%lf\n",l);
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    Init();Solve();return 0;
}

T3

这个题是一个结论题,我们首先考虑这样一件事情,就是说,我们把每个边看做两条 “b”,这里为了区分,我们暂且称作 "b",下面也是一样,然后我们考虑这样一件事情,我们现在对 b 进行染色,每一条边对应的两条 b 我们要分别染上颜色 \(0\) 或颜色 \(1\),然后我们考虑对于一个管道方案来说,有多少中染色方案。

我们称两种染色方案相同,当且仅当对于第一种染色方案的一个管道 \(x,y\),在第二种染色方案中有一个管道 \(x,y\) 与其对应,且这两个管道的染色方式相同。

注意到,如果一个管道方案不存在相同的管道,那么染色方式就是 \(2^{n-1}\),如果存在 \(c\) 对相同的管道,那么染色方式就是 \(2^{n-1-c}\),这是因为我们考虑只有一对管道相同,那么我们交换这两个管道,方案恰好会被算重 \(2\) 倍。

我们发现 \(2^{n-1-c}\) 除以 \(2^{n-1}\) 正好是权值,所以我们只需要算出上面的染色方案,把方案数除以 \(2^{n-1}\) 次方即可。

然后我们如果直接计数这个染色方案并不是很好计算,所以我们考虑看这个染色方案与什么相对应,我们考虑把染色成 \(0\) 的强制放在左边,染色成 \(1\) 的强制放在右边,然后我们发现,一种染色方式就对应着一种边的连接方式,我们考虑直接计数这个链接方式,关注到这个链接方式唯一的限制是一条边上的两个 b 不能相连。

同时注意到,我们每个点都是独立的,也就是说,总的链接方式应该是关注到每一个点,看与每个点相邻的边是怎么相连的,然后把所有点的乘起来。

对于每个点来说相当于这样一个问题:有 \(n\) 对点,现在要把这 \(n\) 对点两两匹配,或者不匹配,有一些限制,限制是以点对的形式出现的,然后一对点之间不能相互匹配,计数上面这个东西额的方案数。

考虑 dp,设 \(f_{i,0/1/2}\) 表示有 \(i\) 对点,还有 \(0/1/2\) 个单点,其中这 \(i\) 对点之间是限制,问这些点两两匹配或不匹配的方案数。

对于 \(f_{i,0}\) 来说,我们考虑最后一对的最后一个点怎么匹配,首先这个点可能不匹配,这样方案就是 \(f_{i-1,1}\),或者匹配,但只能选前面这 \(i-1\) 对点对中的点匹配,匹配之后我们把这一对匹配的点扔掉,考虑剩下的点的方案,这样做是因为不能让匹配上的点再匹配其他点。由此,我们可以得到:

\[f_{i,0}=f_{i-1,1}+f_{i-2,2}\times 2\times (i-1) \]

同理,我们还可以得到:

\[f_{i,1}=f_{i,0}+f_{i-1,1}\times 2\times i\\ f_{i,2}=f_{i,1}+f_{i,0}+f_{i-1,2}\times 2\times i \]

预处理完 dp 之后直接转移即可。

#include<iostream>
#include<cstdio>
#define ll long long
#define N 1000100
#define M number
using namespace std;

const int mod=1e9+7;

int f[N][3],n,ans=1,inv2=500000004,d[N];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++){
		int from,to;scanf("%d%d",&from,&to);d[from]++;d[to]++;
		ans=1ll*ans*inv2%mod;
	}
	f[0][0]=1;f[0][1]=1;f[0][2]=2;
	for(int i=1;i<=n;i++){
		f[i][0]=(f[i-1][1]+1ll*2*(i-1)*f[i-2][2]%mod)%mod;
		f[i][1]=(f[i][0]+1ll*f[i-1][1]*2*i%mod)%mod;
		f[i][2]=((f[i][1]+f[i][0])%mod+1ll*f[i-1][2]*2*i%mod)%mod;
	}
	for(int i=1;i<=n;i++) ans=1ll*ans*f[d[i]][0]%mod;
	printf("%d",ans);
	return 0;
} 

这个题启示我可以尝试计数相似但好计数的情况,然后考虑转化。

posted @ 2021-11-09 18:56  hyl天梦  阅读(39)  评论(0编辑  收藏  举报