CF1394 Div.1 做题记录

A

CF题面

诈骗简单题

看到题被诈骗了,一眼贪心,用一个大于 \(m\) 的数和 \(d+1\) 个小于等于 \(m\) 的数的和比较,选择较大的部分。但被卡掉了。

发现可以直接枚举用 \(x\) 个大于 \(m\) 的数,选的肯定是最大的 \(x\) 个数。这 \(x\) 个数要造成贡献,总共要用掉 \((x-1)\times (d+1)+1\) 个数,先用掉大于 \(m\) 的数,再从小到大用小于等于 \(m\) 的数,剩下的数的和可以用前缀和维护,最后对于所有的答案取 \(\max\) 即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=1e5+10;
int n,d,m,a[N],s[N],q1[N],q2[N],cnt,tot;
bool cmp(int x,int y){
    return x>y;
}
void solve(){
    read(n),read(d),read(m);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    sort(a+1,a+n+1);
    int pos=n+1;
    for(int i=1;i<=n;i++){
        if(a[i]>m){
            q2[++tot]=a[i];
        }
        else{
            q1[++cnt]=a[i];
        }
    }
    sort(a+1,a+cnt+1,cmp);
    for(int i=1;i<=cnt;i++){
        s[i]=s[i-1]+a[i];
    }
    int sum=0,ans=0;
    for(int i=1;i<=tot;i++){
        sum+=a[n-i+1];
        int day=(i-1)*(d+1)+1;
        if(day>n){
            break;
        }
        ans=max(ans,sum+s[min(n-day,cnt)]);
    }
    ans=max(ans,s[cnt]);
    write_endl(ans);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

B

CF题面

先考虑一个怎样的方案是合法的。因为对于每个点都要能从这个点出发,又回到这个点,所以每个点出入度均为 \(1\),又因为根据题意,每个点只有一条出边,所以每个点都在一个环内。

观察到 \(k\) 很小,可以直接 \(n!\) 枚举每个 \(k\) 元组,判断是否合法。对于剩下判合法性的问题,我们想到了[CSP-S 2022] 星战,通过 xor-hashing 判断是否所有点的出边的入度集合为 \(1\sim n\)

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=2e5+10;
int n,m,k,val[N],f[100][100],id[100],S,ans;
mt19937_64 rnd(time(NULL));
struct edge{
    int v,w;
    bool operator <(const edge &rhs){
        return w<rhs.w;
    }
};
void dfs(int i){
    if(i>k){
        int res=0;
        for(int i=1;i<=k;i++){
            res^=f[i][id[i]];
        }
        if(res==S){
            ans++;
        }
        return;
    }
    for(int j=1;j<=i;j++){
        id[i]=j;
        dfs(i+1);
        id[i]=0;
    }
}
vector<edge>e[N];
void solve(){
    read(n),read(m),read(k);
    for(int i=1;i<=m;i++){
        int u,v,w;
        read(u),read(v),read(w);
        e[u].pb(edge{v,w});
    }
    for(int i=1;i<=n;i++){
        val[i]=rnd();
        S^=val[i];
        sort(e[i].begin(),e[i].end());
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=e[i].size();j++){
            f[e[i].size()][j]^=val[e[i][j-1].v];
        }
    }
    dfs(1);
    write_endl(ans);
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

C

CF题面

先做转化,两个字符串相似等同于两个字符串中的字符 BN 的数量均相等。那么我们将一个字符串中的 BN 的数量所组成的二元组 \((x,y)\) 看作它的坐标。那么操作相当于延 \((1,0),(1,1),(0,1),(0,-1),(-1,-1),(-1,0)\) 中的一个方向走一步。当从 \((x,y)\) 开始,走 \(z\) 步,可以到达点组成的图形为一个边长为 \(2z\) 的正方形,去掉左上角和右下角的边长为 \(z\) 的等腰直角三角形。当且仅当这 \(n\) 个图形有交集时,该步数有解。

所以我们考虑二分移动步数,令竖直方向的坐标为 \(y\),水平方向的坐标为 \(x\),斜方向的坐标为 \(x-y\),令其为 \(z\)。得到交集图形在竖直方向上的坐标在 \([yl,yr]\) 内,水平方向上的坐标在 \([xl,xr]\) 内,斜方向上的坐标为 \([zl,zr]\) 内。有解当且仅当三个集合均不为空且 \([zl,zr]\cup[xl-yr,xr-yl]\not=\empty\)

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=5e5+10,inf=1e9;
char s[N];
int x[N],y[N],n,l,r,ansx,ansy;
bool check(int delta){
    int mnx=-inf,mny=-inf,mxx=inf,mxy=inf,mndelta=-inf,mxdelta=inf;
    for(int i=1;i<=n;i++){
        mxx=min(mxx,x[i]+delta);
        mnx=max(mnx,x[i]-delta);
        mxy=min(mxy,y[i]+delta);
        mny=max(mny,y[i]-delta);
        mxdelta=min(mxdelta,x[i]-y[i]+delta);
        mndelta=max(mndelta,x[i]-y[i]-delta);
    }
    if(mxx<mnx||mxy<mny||mxdelta<mndelta||min(mxx-mny,mxdelta)<max(mndelta,mnx-mxy)){
        return 0;
    }
    ansx=min(mxx,mxy+mxdelta);
    ansy=min(mxy,ansx-mndelta);
    return 1;
}
void solve(){
    read(n);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        int len=strlen(s+1);
        r=max(r,len);
        for(int j=1;j<=len;j++){
            x[i]+=(s[j]=='B');
            y[i]+=(s[j]=='N');
        }
    }
    int ans=r;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)){
            r=mid-1;
            ans=mid;
        }
        else{
            l=mid+1;
        }
    }
    write_endl(ans);
    for(int i=1;i<=ansx;i++){
        putchar('B');
    }
    for(int i=1;i<=ansy;i++){
        putchar('N');
    }
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}

D

CF题面

虽然边的方向不定,但我们可以让它定下来。令所有 \(b_u\not=b_v\) 的边 \((u,v)\),边的方向为从 \(b\) 权值大的点到 \(b\) 权值小的点,那么我们只需要处理 \(b_u=b_v\) 的边 \((u,v)\) 即可。

处理这些没定向的边,考虑树形dp。令 \(f_{u,0/1}\) 表示点 \(u\) 和父亲相连的边为 \(u\) 的入边/出边的最小贡献,\(s_1\) 表示有多少确定方向且为 \(u\) 点的出边的边,\(s_2\) 表示有多少确定方向且为 \(u\) 点入边的边,\(s_3\) 表示有多少还没确定方向的边。我们假定这 \(s_3\) 条边中有 \(x\) 条边为 \(u\) 点入边,那么通过将 \(u\) 点的入边和出边相拼接,可以得到 \(u\) 点造成的贡献为 \(\max(s_1+s_3-x,s_2+x)\times a_u\),别忘了处理父边带来的贡献。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define pb push_back
#define eps 1e-9
#define mp make_pair
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;
        int f=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-'){
                f=-1;
    }
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+(ch-'0');
            ch=getchar();
        }
        x=(f==1?x:-x);
    }
    template<typename T>
    inline void write(T x){
        if(x<0){
            putchar('-');
            x=-x;
        }
        if(x>=10){
            write(x/10);
        }
        putchar(x%10+'0');
    }
    template<typename T>
    inline void write_endl(T x){
        write(x);
        putchar('\n');
    }
    template<typename T>
    inline void write_space(T x){
        write(x);
        putchar(' ');
    }
}
using namespace IO;
const int N=2e5+10;
int n,a[N],b[N],f[N][2];
vector<int>e[N];
void dfs(int u,int fa){
    int s=0,s1=0,s2=0;
    vector<int>num;
    for(auto v:e[u]){
        if(v==fa){
            continue;
        }
        dfs(v,u);
        if(b[v]==b[u]){
            num.pb(f[v][1]-f[v][0]);
            s+=f[v][0];
        }
        else if(b[v]>b[u]){
            s+=f[v][1];
            s2++;
        }
        else{
            s+=f[v][0];
            s1++;
        }
    }
    sort(num.begin(),num.end());
    for(int i=0;i<=num.size();i++){
        f[u][0]=min(f[u][0],s+max((int)(s1+num.size()-i),s2+i+(fa>0))*a[u]);
        f[u][1]=min(f[u][1],s+max((int)(s1+num.size()-i)+(fa>0),s2+i)*a[u]);
        if(i<num.size()){
            s+=num[i];
        }
    }
}
void solve(){
    read(n);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    for(int i=1;i<=n;i++){
        read(b[i]);
    }
    for(int i=1,u,v;i<n;i++){
        read(u),read(v);
        e[u].pb(v);
        e[v].pb(u);
    }
    memset(f,0x3f,sizeof(f));
    dfs(1,0);
    write_endl(min(f[1][0],f[1][1]));
}
signed main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
    #endif
    int t=1;
    while(t--){
        solve();
    }
    return 0;
}
posted @ 2023-06-07 09:50  luo_shen  阅读(18)  评论(0)    收藏  举报