11月11日考试 题解(exgcd+树形DP+模拟+最短路+哈希+主席树)

T1 方程的解

题目大意:求$ax+by=c$的正整数解的个数。

exgcd板子。求出来$x$取得最小正整数解时$y$的解;再求出$y$的最小正整数解。两者之差除以$\frac{a}{\gcd (a,b)}$加一即为答案。注意细节。

代码:

#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
int T,a,b,c,x,y,d;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f; 
}
inline void exgcd(int a,int b,int c,int &x,int &y,int &d)
{
    if(!b) y=0,d=a,x=c/a;
    else  exgcd(b,a%b,c,y,x,d),y-=(a/b)*x;
}
signed main()
{
    T=read();
    while(T--)
    {
        a=read(),b=read(),c=read();
        if (!a&&!b)
        {
            if (!c) puts("ZenMeZheMeDuo");
            else puts("0");
            continue;
        }
        if (c<0) a=-a,b=-b,c=-c;
        bool fa=0,fb=0;
        if (a<0) a=-a,fa=1;
        if (b<0) b=-b,fb=1;
        exgcd(a,b,c,x,y,d);
        if (a*x+b*y!=c){puts("0");continue;}
        if (fa) a=-a,x=-x;
        if (fb) b=-b,y=-y;
        if (a==0)
        {
            if (y<=0) puts("0");
            else puts("ZenMeZheMeDuo");
            continue;
        }
        if (b==0)
        {
            if (x<=0) puts("0");
            else puts("ZenMeZheMeDuo");
            continue;
        }
        if (a*b<0){puts("ZenMeZheMeDuo");continue;}
        if(a<0) a=-a,b=-b,c=-c;
        a/=d;b/=d;c/=d;x%=b;
        if(x<=0) x+=b;
        y=(c-a*x)/b;
        int miny=y%a;
        if(miny<=0) miny+=a;
        int res=(miny>y)?0:(y-miny)/a+1;
        if(res>65535) puts("ZenMeZheMeDuo");
        else printf("%lld\n",res);
    }
    return 0;
}

T2 染色

原题目:P3177。

设$f_{i,j}$表示$i$为根的子树内有$j$个黑点对答案的贡献。然后大力转移就好。可以参考题解。

代码:

#include<bits/stdc++.h>
#define ll long long
#define gc getchar
#define maxn 2005
using namespace std;
inline ll read(){
    ll a=0;int f=0;char p=gc();
    while(!isdigit(p)){f|=p=='-';p=gc();}
    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
    return f?-a:a;
}
struct ahaha{
    int w,to,next;
}e[maxn<<1];int tot,head[maxn];
inline void add(int u,int v,int w){
    e[tot].w=w,e[tot].to=v,e[tot].next=head[u];head[u]=tot++;
}
int n,m,sz[maxn];
ll f[maxn][maxn];
void dfs(int u,int fa){
    sz[u]=1;f[u][0]=f[u][1]=0;
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;if(v==fa)continue;
        dfs(v,u);sz[u]+=sz[v];
        for(int j=min(m,sz[u]);j>=0;--j){  
            if(f[u][j]!=-1)    
                f[u][j]+=f[v][0]+(ll)sz[v]*(n-m-sz[v])*e[i].w;
            for(int k=min(j,sz[v]);k;--k){
                if(f[u][j-k]==-1)continue;
                ll val=(ll)(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w;
                f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+val);
            }
        }
    }
}
int main(){memset(head,-1,sizeof head);
    n=read();m=read();
    if(n-m<m)m=n-m;
    for(int i=1;i<n;++i){
        int u=read(),v=read(),w=read();
        add(u,v,w);add(v,u,w);
    }memset(f,-1,sizeof f);
    dfs(1,-1);
    printf("%lld",f[1][m]);
    return 0;
}

T3 光

原题目:CF274E

大力模拟。先考虑暴力怎么做:每次按照光线的方向dfs,直到碰到一个障碍点再反射。这样寻找复杂度是$O(n)$的。而我们现在要将其优化成$\log n$。具体做法是把所有对角线编号,对于障碍点扔进STL::set里面,查询的时候lower_bound查找。然后就是一些细节了。考试时候好不容易想出来了做法结果写挂了,还不如交暴力QAQ

代码:

#include<set>
#include<map>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=200005;
int n,m,k,sx,sy,d;
char ch[5];
long long ans;
set<int> s1[N],s2[N];
map<pair<int,int>,bool> mp;
struct node{
    int x,y,d;
};
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline int get(int x,int y,int d){
    return d==1?x-y+m+1:x+y;
}
inline bool same(node a,node b){
    return a.x==b.x&&a.y==b.y&&a.d==b.d;
}
inline bool check(int x,int y){
    return mp[make_pair(x,y)];
}
inline void add(int x,int y)
{
    s1[get(x,y,1)].insert(x);
    s2[get(x,y,2)].insert(x);
    mp[make_pair(x,y)]=1;
}
//NE(-1,1) NW(-1,-1) SE(1,1) SW(1,-1)
//1:NW 2:NE 3:SE 4:SW
//d:from which dinodetion
inline pair<node,int> dfs(node u)
{
    node res;
    set<int>::iterator it;
    if(u.d==1)
    {
        it=s1[get(u.x,u.y,1)].lower_bound(u.x);it--;
        res.x=u.x-(abs(*it-u.x)-1);
        res.y=u.y-(abs(*it-u.x)-1);
        if(check(res.x-1,res.y)&&check(res.x,res.y-1))res.d=3;
        else if(check(res.x-1,res.y)){res.y--;res.d=4;}
        else if(check(res.x,res.y-1)){res.x--;res.d=2;}
        else res.d=3;
    }
    if(u.d==2)
    {
        it=s2[get(u.x,u.y,2)].lower_bound(u.x);it--;
        res.x=u.x-(abs(*it-u.x)-1);
        res.y=u.y+(abs(*it-u.x)-1);
        if(check(res.x-1,res.y)&&check(res.x,res.y+1))res.d=4;
        else if(check(res.x-1,res.y)){res.y++;res.d=3;}
        else if(check(res.x,res.y+1)){res.x--;res.d=1;}
        else res.d=4;
    }
    if(u.d==3)
    {    
        it=s1[get(u.x,u.y,1)].lower_bound(u.x);
        res.x=u.x+(abs(*it-u.x)-1);
        res.y=u.y+(abs(*it-u.x)-1);
        if(check(res.x+1,res.y)&&check(res.x,res.y+1))res.d=1;
        else if(check(res.x+1,res.y)){res.y++;res.d=2;}
        else if(check(res.x,res.y+1)){res.x++;res.d=4;}
        else res.d=1;
    }
    if(u.d==4)
    {
        it=s2[get(u.x,u.y,2)].lower_bound(u.x);
        res.x=u.x+(abs(*it-u.x)-1);
        res.y=u.y-(abs(*it-u.x)-1);
        if(check(res.x+1,res.y)&&check(res.x,res.y-1))res.d=2;
        else if(check(res.x+1,res.y)){res.y--;res.d=1;}
        else if(check(res.x,res.y-1)){res.x++;res.d=3;}
        else res.d=2;
    }
    return make_pair(res,abs(*it-u.x));
}
bool judge(node u)
{
    node res=u;
    do
    {
        pair<node,int> cur=dfs(u);
        ans+=(long long)cur.second;
        switch(cur.first.d)
        {
            case 1:if(u.d==3)return 0;break;
            case 2:if(u.d==4)return 0;break;
            case 3:if(u.d==1)return 0;break;
            case 4:if(u.d==2)return 0;break;
        }
        u=cur.first;
    }while(!same(res,u));
    return 1;
}
int main()
{
    n=read();m=read();k=read();
    for (int i=0;i<=m+1;i++) add(0,i),add(n+1,i);
    for (int i=0;i<=n+1;i++) add(i,0),add(i,m+1);
    for(int i=1;i<=k;i++)
    {
        int x=read(),y=read();
        add(x,y);
    }
    int x=read(),y=read(),d;
    char ch[5];scanf("%s",ch+1);
    if(ch[1]=='N'&&ch[2]=='W')d=1;
    if(ch[1]=='N'&&ch[2]=='E')d=2;
    if(ch[1]=='S'&&ch[2]=='E')d=3;
    if(ch[1]=='S'&&ch[2]=='W')d=4;
    node st={x,y,d};
    st=dfs(st).first;
    if(!judge(st))
    {
        ans--;
        switch(st.d)
        {
            case 1:st.d=3;break;
            case 2:st.d=4;break;
            case 3:st.d=1;break;
            case 4:st.d=2;break;
        }
        judge(st);
    }
    printf("%lld",ans);
    return 0;
}

T4 无向图

题目大意:给定无向图G =(V,E),其中V={1,2,.....n },以及函数p:V →V和f:V →V,
保证 {p(i)|i∈V }= V。对于 G 的任意路径 P 和 v∈V ,记
C(P,v)=u∈V(P)[f(u)=v] ,其中V(P)为路径 P 经过的点集(包括起点和终点)。
对于两条路径P1和P2, P1和P2的比较方法如下:取最小的i,满足C(P1,p(i))
C(P2,p(i)),则定义C(Pj ,p(i)) 较大的那条路径 Pj较大。若所有C(P1,p(i))=
C(P2,p(i)),则认为 P1 和 P2 一样大。对于给定的无向图G =(V ,E)及函数 p:V → V 和 f : V → V ,求以1为起
点、 n为终点的所有路径中最小的一条。

题面很诡异。大概意思就是对于路径上的所有点,把它们$f$值扔进桶里面,然后按照$p$依次比较字典序。找到字典序最小的那个并输出桶内的值。

暴力的做法是直接跑最短路,暴力比较。这样修改的复杂度是$O(1)$的,比较的复杂度$O(n)$。我们可以尝试平衡复杂度。一个比较套路的方法是利用主席树。更新属于继承过来,比较实际上可以把两条路径的字典序哈希起来比较。这样修改和查询的复杂度都是$\log n$的。

代码:

#include<queue>
#include<cstdio>
#include<iostream>
#define ull unsigned long long
using namespace std;
const int base=131;
const int N=100005,M=500005;
int n,m,t,p[N],f[N],rev[N];
int rt[N],ls[N*21],rs[N*21],cnt[N*21],tot;
ull b[N],val[N*21];
int head[N],edge_cnt;
bool vis[N],tag[N];
struct e{
    int next,to;
}edge[M*2];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void add(int from,int to)
{
    edge[++edge_cnt]=(e){head[from],to};
    head[from]=edge_cnt;
}
inline void update(int &cur,int l,int r,int pos)
{
    int x=++tot;
    ls[x]=ls[cur],rs[x]=rs[cur];
    cnt[x]=cnt[cur]+1,val[x]=val[cur]+b[pos],cur=x;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) update(ls[cur],l,mid,pos);
    else update(rs[cur],mid+1,r,pos);
}
inline int query(int x,int y,int l,int r)
{
    ull v=val[y];
    int c=cnt[y];
    if(t&&t>=l&&t<=r) v+=b[t],c++;
    if(val[x]==v) return 0;
    if(!cnt[x]) return -1;
    if(!c) return 1;
    if(l==r)
    {
        if(cnt[x]==c) return 0;
        if(cnt[x]>c) return 1;
        if(cnt[x]<c) return -1;
    }
    int mid=(l+r)>>1;
    int q=query(ls[x],ls[y],l,mid);
    if(q) return q;
    return query(rs[x],rs[y],mid+1,r);
}
struct node{
    int id;
};
bool operator < (const node &a,const node &b){
    return query(rt[a.id],rt[b.id],1,n)>=0;
}
inline void dijkstra()
{
    priority_queue<node> q;
    update(rt[1],1,n,rev[f[1]]);q.push({1});tag[1]=1;
    while(!q.empty())
    {
        int x=q.top().id;
        q.pop();
        if(vis[x]) continue;
        vis[x]=true;
        for(int i=head[x];i;i=edge[i].next)
        {
            int y=edge[i].to;
            if(!tag[y]) tag[y]=true,rt[y]=rt[x],update(rt[y],1,n,rev[f[y]]),q.push({y});
            else
            {
                t=rev[f[y]];
                if(query(rt[y],rt[x],1,n)==1)
                    rt[y]=rt[x],update(rt[y],1,n,rev[f[y]]),q.push({y});
                t=0;
            }
        }
    }
}
inline void dfs(int cur,int l,int r)
{
    if(!cur) return;
    if(l==r)
    {
        for(int i=1;i<=cnt[cur];++i) printf("%d ",p[l]);
        return;
    }
    int mid=(l+r)>>1;
    dfs(ls[cur],l,mid),dfs(rs[cur],mid+1,r);
}
int main()
{
    n=read(),m=read(),b[0]=1;
    for(int i=1;i<=n;++i) b[i]=b[i-1]*base;
    for(int i=1;i<=n;++i) p[i]=read(),rev[p[i]]=i;
    for(int i=1;i<=n;++i) f[i]=read();
    for(int i=1;i<=m;++i)
    {
        int x=read(),y=read();
        add(x,y),add(y,x);
    }
    dijkstra(),dfs(rt[n],1,n);
    return 0;
}

 

posted @ 2020-11-11 17:23  我亦如此向往  阅读(161)  评论(0编辑  收藏  举报