AGC025做题笔记


不知道为啥想整骑砍了,回去把维京征服给重新下载一下好了。。。


A:暴力枚举完事


B:计数题+小trick
一开始犯病了没想出来
实际上可以发现 \(A\)\(B\) 之间是相互独立的,因此直接把两次操作分开来看,枚举涂成 \(A\) 的个数然后就是简单组合数。

Code


#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
    bool f=true;ll x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(f) return x;
    return ~(--x);
}
il int read(char *s){
    int len=0;
    register char ch=getchar();
    while(ch==' '||ch=='\n') ch=getchar();
    while(ch!=' '&&ch!='\n'&&ch!=EOF) s[++len]=ch,ch=getchar();
    return len;
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
const int MAXN=3e5+7;
ll n,a,b,k;
const ll mod =998244353;
il ll ksm(ll d,ll tim){
    ll rest=1;
    while(tim){
        if(tim&1) rest=rest*d%mod;
        d=d*d%mod;
        tim>>=1;
    }
    return rest;
}
ll jc[MAXN],inv[MAXN];
void init(int n=MAXN-1){
    jc[0]=1;
    for(ri i=1;i<=n;++i) jc[i]=i*jc[i-1]%mod;
    inv[n]=ksm(jc[n],mod-2);
    for(ri i=n-1;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
il ll C(ll x,ll y){
    if(x<y) return 0;
    return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
ll ans;
int main(){
    init();
    n=read(),a=read(),b=read(),k=read();
    for(ri i=0;i<=n&&a*i<=k;++i){
        if((k-a*i)%b) continue;
        ans=(ans+C(n,i)*C(n,(k-a*i)/b))%mod;
    }
    print(ans);
    return 0;
}

C:贪心
非常显然的可以交叉着选当前右端点最左边的和左端点最右边的,枚举第一次是在左边还是右边然后用堆来维护。

Code

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
    bool f=true;ll x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(f) return x;
    return ~(--x);
}
il int read(char *s){
    int len=0;
    register char ch=getchar();
    while(ch==' '||ch=='\n') ch=getchar();
    while(ch!=' '&&ch!='\n'&&ch!=EOF) s[++len]=ch,ch=getchar();
    return len;
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
const int MAXN=1e5+7;
int mark[MAXN],n;
#define pll pair<ll,ll>
#define fir first
#define sec second
priority_queue<pll,vector<pll>,greater<pll> > l;
priority_queue<pll> r;
ll ans,res;
struct node
{
    ll L,R;
}a[MAXN];
il ll getdis(int &pos,int x){
    int res=0;
    if(pos>=a[x].R){
        res=pos-a[x].R;
        pos=a[x].R;
    }
    else if(pos<=a[x].L){
        res=a[x].L-pos;
        pos=a[x].L;
    }
    return res;
}
int main(){
    n=read();
    for(ri i=1;i<=n;++i) a[i].L=read(),a[i].R=read();
    memset(mark,0,sizeof(mark));
    for(ri i=1;i<=n;++i) l.push((pll){a[i].R,i}),r.push((pll){a[i].L,i});
    int pos=0;
    for(ri i=1,o=0;i<=n;++i,o^=1){
        if(o){
            while((mark[r.top().sec])) r.pop();
            mark[r.top().sec]=1;
            res+=getdis(pos,r.top().sec);
            r.pop();
        }
        else{
            while((mark[l.top().sec])) l.pop();
            mark[l.top().sec]=1;
            res+=getdis(pos,l.top().sec);
            l.pop();
        }
    }
    res+=abs(pos);
    ans=res;
    res=0;
    while(!l.empty()) l.pop();
    while(!r.empty()) r.pop();
    memset(mark,0,sizeof(mark));
    for(ri i=1;i<=n;++i) l.push((pll){a[i].R,i}),r.push((pll){a[i].L,i});
    pos=0;
    for(ri i=1,o=1;i<=n;++i,o^=1){
        if(o){
            while((mark[r.top().sec])) r.pop();
            mark[r.top().sec]=1;
            res+=getdis(pos,r.top().sec);
            r.pop();
        }
        else{
            while((mark[l.top().sec])) l.pop();
            mark[l.top().sec]=1;
            res+=getdis(pos,l.top().sec);
            l.pop();
        }
    }
    res+=abs(pos);

    ans=max(ans,res);
    print(ans);
    return 0;
}

D:挺妙的构造

先把不满足条件的点之间互相连边,可以发现对于每一种限制来说都是一张二分图。
一共两种限制,所以共有四种颜色,根据鸽巢原理一定会有一种颜色至少有 \(\frac{n}{4}\) 个点。

Code

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
    bool f=true;ll x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(f) return x;
    return ~(--x);
}
il int read(char *s){
    int len=0;
    register char ch=getchar();
    while(ch==' '||ch=='\n') ch=getchar();
    while(ch!=' '&&ch!='\n'&&ch!=EOF) s[++len]=ch,ch=getchar();
    return len;
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
ll a,b;
#define pll pair<ll,ll>
#define fir first
#define sec second
#define pb push_back
int n;
const int MAXN=605;
vector<pll> f,p[4],g[MAXN][MAXN];
int mark[MAXN][MAXN];
bool vis[MAXN][MAXN];
void dfs(int x,int y,int now,int base){
    if(vis[x][y]) return;
    vis[x][y]=1;
    mark[x][y]|=now;
    for(ri i=0;i<g[x][y].size();++i){
        pll e=g[x][y][i];
        dfs(e.fir,e.sec,now^base,base);
    }
}
int main(){
    n=read(),a=read(),b=read();
    for(ri i=0;i*i<=a;++i){
        int j=sqrt(a-i*i);
        if(j*j+i*i==a){
            f.pb((pll){i,j});
            f.pb((pll){i,-j});
            f.pb((pll){-i,j});
            f.pb((pll){-i,-j});
        }
    }
    for(ri i=0;i<2*n;++i){
        for(ri j=0;j<2*n;++j){
            for(ri k=0;k<f.size();++k){
                int x=i+f[k].fir,y=j+f[k].sec;
                if(x<0||x>=2*n||y<0||y>=2*n) continue;
                g[i][j].push_back((pll){x,y});
                g[x][y].push_back((pll){i,j});
            }
        }
    }
    memset(vis,0,sizeof(vis));
    for(ri i=0;i<2*n;++i){
        for(ri j=0;j<2*n;++j){
            if(vis[i][j]) continue;
            dfs(i,j,0,1);
        }
    }
    for(ri i=0;i<2*n;++i){
        for(ri j=0;j<2*n;++j){
            g[i][j].clear();
        }
    }
    f.clear();
    for(ri i=0;i*i<=b;++i){
        int j=sqrt(b-i*i);
        if(j*j+i*i==b){
            f.pb((pll){i,j});
            f.pb((pll){i,-j});
            f.pb((pll){-i,j});
            f.pb((pll){-i,-j});
        }
    }
    for(ri i=0;i<2*n;++i){
        for(ri j=0;j<2*n;++j){
            for(ri k=0;k<f.size();++k){
                int x=i+f[k].fir,y=j+f[k].sec;
                if(x<0||x>=2*n||y<0||y>=2*n) continue;
                g[i][j].push_back((pll){x,y});
                g[x][y].push_back((pll){i,j});
            }
        }
    }
    memset(vis,0,sizeof(vis));
    for(ri i=0;i<2*n;++i){
        for(ri j=0;j<2*n;++j){
            if(vis[i][j]) continue;
            dfs(i,j,0,2);
        }
    }
    for(ri i=0;i<2*n;++i){
        for(ri j=0;j<2*n;++j){
            p[mark[i][j]].pb((pll){i,j});
        }
    }
    for(ri i=0;i<4;++i){
        if(p[i].size()>=n*n){
            for(ri j=0;j<n*n;++j){
                printf("%lld %lld\n",p[i][j].fir,p[i][j].sec);
            }
            return 0;
        }
    }
    return 0;
}

E:构造+结论
之前寒假的时候有学长讲过这题,所以结论很快就猜出来了。
首先可以发现答案的上界是\(\sum \min(2,c_{u,v})\) ,其中 \(c_{u,v}\) 是这条边 \((u,v)\) 被路径覆盖的次数。
考虑如何构造。
首先对于 \(c_{u,v}=1\) 的边,无论路径方向如何贡献都是 \(1\),所以不用考虑,可以直接删掉这个点。
对于 \(c_{u,v}\geq 2\) 的边,假设现在路径的起点是 \(u\) , \(u\) 的父亲是 \(v\),那么可以随便选两条路径出来,设其为 \((u,x),(u,y)\)
假设它们在 \(z\) 处分岔了,那么就可以把这两条路径拆成 \((u,z),(z,u),(x,y)\) ,很显然是等价的,其中前面的 \((u,z),(z,u)\) 则能直接让 \(u\)\(z\) 这一条链上所有的点答案都为 \(2\),而问题则变成了一个子问题,不难发现删掉 \((u,z)\) 这一条链上所有的点不会影响答案,而 \((u,x),(u,y)\) 的方向则由 \((x,y)\) 来决定了。
代码比较恶心,咕了。


F:进制题+结论题
首先看到题目,可以毛估估一下,高位是不会影响低位的,所以可以从高位往低位枚举,如果 \(a_i=b_i=1\)\(a_{i}++,b_{i}++\),再从 \(i\) 开始往高位枚举判断是否进位。
如果因为进位导致了前面出现了 \(a_j=b_j=1\) ,则继续上面的操作。
这样操作 \(k\) 次则与题目要求等价了。

然后因为数据太水,直接这样模拟能过
考虑加速这个过程。
可以发现中间有一些 \(a_j=b_j=0\) 的点,这些地方其实上是可以直接加速的,只有当 \(a_j,b_j\) 中有一个是 \(1\) 的时候才需要进位,而每这样进位一次都会让 \(1\) 的个数减少一个。
于是,用一个栈来统计所有 \(a_j+b_j=1\) 的点,然后模拟上面的过程就行了。
每一次进位是 \(O(1)\) 的,而 \(1\) 的总数是 \(O(n)\),所以总复杂度是 \(O(n)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define ll long long
#define ui unsigned int
il ll read(){
    bool f=true;ll x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=false;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(f) return x;
    return ~(--x);
}
il void write(const ll &x){if(x>9) write(x/10);putchar(x%10+'0');}
il void print(const ll &x) {x<0?putchar('-'),write(~(x-1)):write(x);putchar('\n');}
il ll max(const ll &a,const ll &b){return a>b?a:b;}
il ll min(const ll &a,const ll &b){return a<b?a:b;}
const int MAXN=2e6+7;
char a[MAXN],b[MAXN];
int sta[MAXN],top,vec[MAXN],head;
ll n,m,k,lst;
int main(){
    //freopen("rand.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read(),m=read(),k=read();
    scanf("%s",a+1);
    reverse(a+1,a+n+1);
    for(ri i=1;i<=n;++i) a[i]-='0';
    scanf("%s",b+1);
    reverse(b+1,b+m+1);
    for(ri i=1;i<=m;++i) b[i]-='0';
    for(ri i=max(n,m);i;--i){
        int pos=i,res=k;
        while(top&&sta[top]-pos<=res&&a[pos]&&b[pos]){
            res-=sta[top]-pos;
            a[pos]=b[pos]=0;
            int now=sta[top];
            top--;
            a[now]++;
            b[now]++;
            for(ri j=now;a[j]||b[j];++j){
                while(top&&sta[top]<=j) --top;
                if(a[j]>1||b[j]>1||(a[j]==1&&b[j]==1)){
                    if(a[j]>1){
                        a[j+1]++;
                        a[j]=0;
                    }
                    if(b[j]>1){
                        b[j+1]++;
                        b[j]=0;
                    }
                    if(a[j]&&b[j]){
                        pos=j;
                        break;
                    }
                    else if(a[j]||b[j]) vec[++head]=j;
                }
                else{
                    if(a[j]||b[j]) vec[++head]=j;
                    break;
                }
            }
        }
        if(a[pos]&&b[pos]) a[pos]=b[pos]=0,a[pos+res]=b[pos+res]=1;
        while(head) sta[++top]=vec[head--];
        if(a[pos]+b[pos]==1&&sta[top]!=pos) sta[++top]=pos;
    }
    top=n+k;
    while(!a[top]) --top;
    for(ri i=top;i;--i) 
        putchar('0'+a[i]);
    puts("");
    top=m+k;
    while(!b[top]) --top;
    for(ri i=top;i;--i) 
        putchar('0'+b[i]);
    return 0;
}
posted @ 2021-04-12 19:55  krimson  阅读(68)  评论(0)    收藏  举报