屯题1

题目屯着,满了50道写一波题解

分5篇写,每篇10道这样 

 

1.UOJ118  赴京赶考

考虑a[i]!=a[i+1],那么无论哪一行,这两个相邻的走过去都需要1的代价

同样的b[i]!=b[i+1],那么无论哪一列,这两个相邻的走过去都需要1的代价

所以(x,y)走到(xx,yy)等价于x走到xx的最小代价(1维情况即可)

+y走到yy的最小代价(也是1维情况)

维护一个前缀和就好了

代码如下

#include<bits/stdc++.h>
#define N 500005
using namespace std;
int n,m,Q,xc,xs,yc,ys,a[N],b[N],sumh[N],suml[N];
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) scanf("%d",&b[i]);
    for (int i=2;i<=n;i++) sumh[i]=sumh[i-1]+(a[i]!=a[i-1]);
    for (int i=2;i<=m;i++) suml[i]=suml[i-1]+(b[i]!=b[i-1]);
    scanf("%d",&Q);
    while (Q--){
        scanf("%d%d%d%d",&xs,&ys,&xc,&yc);
        if (xs>xc) swap(xs,xc);
        if (ys>yc) swap(ys,yc);
        int tmp=sumh[xc]-sumh[xs];
        tmp=min(tmp,sumh[n]-sumh[xc]+(a[1]!=a[n])+sumh[xs]);
        int ans=tmp;
        tmp=suml[yc]-suml[ys];
        tmp=min(tmp,suml[m]-suml[yc]+(b[1]!=b[m])+suml[ys]);
        printf("%d\n",tmp+ans);
    }
    return 0;
}

 

2.UOJ48

把a[1]的所有质因数求出来

我们发现次大公约数=最大公约数/共有的最小质因数

代码如下:

#include<bits/stdc++.h>
#define int long long
#define N 500005
using namespace std;
int n,cnt,a[N],num[N];
int gcd(int x,int y){if(!y)return x;return gcd(y,x%y);}
signed main(){
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    int xb=a[1];cnt=0;
    for (int i=2;i*i<=xb;i++){
        if (a[1]%i) continue;
        while (a[1]%i==0) a[1]/=i;
        num[++cnt]=i;
    }
    if (a[1]!=1) num[++cnt]=a[1];
    a[1]=xb;
    for (int i=1;i<=n;i++){
        int G=gcd(a[i],xb);int j;
        if(G==1){printf("-1 ");continue;}
        for (j=1;j<=cnt;j++){
            if (G%num[j]) continue;
            else break;
        }
        printf("%lld ",G/num[j]);
    }
    return 0;
}

 3.UOJ31

思维题

考虑一个右括号,它找到离它最近的,并且没有被选定的左括号交换

因为都是n个所以一定能找到

那么这么交换一定能保证最后序列合法

左括号被选定的位置是单调的

所以O(n)扫一遍就好了

#include<bits/stdc++.h>
#define N 500005
using namespace std;
struct node{int l,r;}a[N];
int n,cnt;char s[N];
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    int now=1,last=0;
    for (int i=1;i<=n;i++) if (s[i]=='(') last=max(last,i);
    for (int i=1;i<=n;i++){
        if (s[i]==')'){
            now=max(now,i);
            if (now>last) break;
            a[++cnt].l=i;
            for (;now<=n;now++){
                if (s[now]=='('){
                    a[cnt].r=now;
                    now++;
                    break;
                }
            }
        }
    }
    printf("%d\n",cnt);
    for (int i=1;i<=cnt;i++) printf("%d %d\n",a[i].l,a[i].r);
    return 0;
}

 

4.UOJ30

圆方树好题

我们考虑每一个方点只维护它所有儿子的信息

这样的话修改就是logn的了

查询的时候,考虑(u,v),如果u和v的lca是方点

那么再算一下lca的父亲的贡献

如果是圆点那么一定考虑完全了,因为方点的父亲一定被包括算进去了

代码如下:

#include<bits/stdc++.h>
#define N 2000005
using namespace std;
int sz[N],head[N],heavyson[N],top[N],dfn[N],low[N],d[N];char ss[5];
int rel[N],id[N],father[N],w[N],s[N],dep[N],n,m,Q,x,y,topp,kk,tim1,cnt;
vector<int>G[N];
multiset<int>S[N];
struct Edge{int nxt,to;}e[N];
inline void link(int x,int y){e[++kk].nxt=head[x];e[kk].to=y;head[x]=kk;}
void tarjan(int u,int fa){
    dfn[u]=low[u]=++tim1;s[++topp]=u;
    for (int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if (v==fa) continue;
        if (!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]){//说明u是割点,v所在的点双到u为止,u以上的都不能在这个点双内 
                cnt++;G[u].push_back(cnt);
                while (s[topp]!=v){
                    G[cnt].push_back(s[topp]);
                    S[cnt].insert(w[s[topp]]);
                    topp--;
                }
                G[cnt].push_back(s[topp]);
                S[cnt].insert(w[s[topp]]);
                topp--;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
void dfs1(int u,int fa){
    sz[u]=1;
    for (int i=0;i<(int)G[u].size();i++){
        int v=G[u][i];
        if (v==fa) continue;
        dep[v]=dep[u]+1;
        father[v]=u;
        dfs1(v,u);
        sz[u]+=sz[v];
        if (!heavyson[u]||sz[v]>sz[heavyson[u]]) heavyson[u]=v;
    }
}
void dfs2(int u,int first){
    top[u]=first;id[u]=++tim1;
    rel[tim1]=u;
    if (!heavyson[u]) return;
    dfs2(heavyson[u],first);
    for (int i=0;i<(int)G[u].size();i++){
        int v=G[u][i];
        if (v==father[u]||v==heavyson[u]) continue;
        dfs2(v,v);
    }
}
inline int lca(int x,int y){
    while (top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        x=father[top[x]];
    }
    if (dep[x]<dep[y]) return x;return y;
}
void build(int k,int l,int r){
    if (l==r){
        int x=rel[l];
        if (x>n) d[k]=*S[x].begin();
        else d[k]=w[x];
        return;
    }
    int mid=(l+r)>>1;
    build(k*2,l,mid);build(k*2+1,mid+1,r);
    d[k]=min(d[k*2],d[k*2+1]);
}
void update(int k,int l,int r,int x,int y){
    if (l==r){d[k]=y;return;}
    int mid=(l+r)>>1;
    if (x<=mid) update(k*2,l,mid,x,y);
    else update(k*2+1,mid+1,r,x,y);
    d[k]=min(d[k*2],d[k*2+1]);
}
void update1(int k,int l,int r,int x,int y,int z){
    if (l==r){
        int u=rel[l];
        S[u].erase(S[u].find(y));
        S[u].insert(z);
        int mn=*S[u].begin();
        update(1,1,cnt,id[u],mn);
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) update1(k*2,l,mid,x,y,z);
    else update1(k*2+1,mid+1,r,x,y,z);
    d[k]=min(d[k*2],d[k*2+1]);
}
int getpos(int k,int l,int r,int x){
    if (l==r) return d[k];
    int mid=(l+r)>>1;
    if (x<=mid) return getpos(k*2,l,mid,x);
    else return getpos(k*2+1,mid+1,r,x);
}
int query(int k,int l,int r,int x,int y){
    if (x<=l&&y>=r) return d[k];
    int mid=(l+r)>>1;
    if (y<=mid) return query(k*2,l,mid,x,y);
    else if (x>mid) return query(k*2+1,mid+1,r,x,y);
    else return min(query(k*2,l,mid,x,mid),query(k*2+1,mid+1,r,mid+1,y));
}
int Query(int x,int y){
    int ans=1e9;
    while (top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        ans=min(ans,query(1,1,cnt,id[top[x]],id[x]));
        x=father[top[x]];
    }
    if (dep[x]<dep[y]) swap(x,y);
    ans=min(ans,query(1,1,cnt,id[y],id[x]));
    return ans;
}
int main(){
    scanf("%d%d%d",&n,&m,&Q);
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        link(x,y);link(y,x);
    }
    cnt=n;tarjan(1,-1);tim1=0;
    dfs1(1,-1);dfs2(1,1);
    build(1,1,cnt);
    while (Q--){
        scanf("%s%d%d",ss+1,&x,&y);
        if (ss[1]=='C'){
            update(1,1,cnt,id[x],y);
            if (father[x]>n) update1(1,1,cnt,id[father[x]],w[x],y);
            w[x]=y;
        }
        else {
            int LCA=lca(x,y);
            int ans=1e9;
            if (LCA>n&&father[LCA]) ans=getpos(1,1,cnt,id[father[LCA]]);
            ans=min(ans,Query(x,y));
            printf("%d\n",ans);
        }
    }
    return 0;
}

 5.UOJ22

我们发现Mod有一个性质

x%y后的值%z,如果(y<z)那么不会有任何改变

所以把a从大到小排个序

用dp[i][j]表示前i个,当前值为j是否可行

然后g[i][j]记录方案数即可

#include<bits/stdc++.h>
#define Mod 998244353
#define N 5005
using namespace std;
int n,X,a[N],g[N][N];bool dp[N][N];
int main(){
    scanf("%d%d",&n,&X);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);reverse(a+1,a+n+1);
    dp[0][X]=1;g[0][X]=1;
    for (int i=1;i<=n;i++){
        for (int j=0;j<=X;j++){
            dp[i][j%a[i]]|=dp[i-1][j];
            g[i][j%a[i]]+=g[i-1][j];
            g[i][j%a[i]]%=Mod;
        }
        if (i==n) break;
        for (int j=0;j<=X;j++){
            dp[i][j]|=dp[i-1][j];
            g[i][j]=1ll*(g[i][j]+1ll*g[i-1][j]*(n-i)%Mod)%Mod;
        }
    }
    for (int i=X;~i;i--)
        if (dp[n][i]){
            printf("%d\n",i);
            printf("%d\n",g[n][i]);
            return 0;
        }
    return 0;
}

 6.UOJ14

我们发现加入的边权一直是变大的

所以如果之前i,j之间右边,那么这个就是来卖萌的

然后记录一下每条边是否被用

如果被用,删除了就gg了

大概用一个可撤销并查集维护一下就行了

代码如下:

#include<bits/stdc++.h>
#define N 500005
#define int long long
using namespace std;
char sss[15];
int n,m,top,fa[N],sz[N],num[N],sum[N],op1[N],op2[N],op3[N],opt[N],x[N],y[N];
int find(int x){if(x==fa[x])return x;return find(fa[x]);}
inline void link(int L,int R,int step){
    top++;num[top]=num[top-1];sum[top]=sum[top-1];
    op1[top]=op2[top]=op3[top]=0;
    int fx=find(L);int fy=find(R);
    if (fx==fy) return;
    if (sz[fx]<sz[fy]) swap(fx,fy);
    op1[top]=1;op2[top]=sz[fx];op3[top]=fy;
    sz[fx]+=sz[fy];
    fa[fy]=fx;
    num[top]++;sum[top]+=step;
}
inline void del(int tot){
    while (tot--){
        if (op1[top]){
            int fy=op3[top];
            int fx=fa[fy];
            fa[fy]=fy;
            sz[fx]=op2[top];
            num[top]--;
        }top--;
    }
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for (int i=1;i<=n;i++) fa[i]=i,sz[i]=1;
    for (int i=1;i<=m;i++){
        scanf("%s",sss+1);
        if (sss[1]=='A') opt[i]=1;
        else if (sss[1]=='D') opt[i]=2;
        else opt[i]=3;
        if (opt[i]==1) scanf("%lld%lld",&x[i],&y[i]);
        else if (opt[i]==2) scanf("%lld",&x[i]);
    }
    for (int i=1;i<=m;i++){
        if (opt[i]==1){
            link(x[i],y[i],i);
            if (num[top]==n-1) printf("%lld\n",sum[top]);
            else puts("0");
        }
        else if (opt[i]==2){
            if (opt[i+1]==3){
                if (num[top-x[i]]==n-1) printf("%lld\n",sum[top-x[i]]);
                else puts("0");
            }
            else {
                del(x[i]);
                if (num[top]==n-1) printf("%lld\n",sum[top]);
                else puts("0");
            }
        }
        else if (opt[i]==3){
            if (opt[i-1]==1) del(1);
            if (num[top]==n-1) printf("%lld\n",sum[top]);
            else puts("0");
        }
    }
    return 0;
}

 7.CF963A

很无聊的等比数列题

代码如下:

#include<bits/stdc++.h>
#define N 600005
#define Mod 1000000009
#define int long long
using namespace std;
int n,a,b,k,a1;char s[N];
inline int ksm(int x,int y){
    int ans1=1;while (y){
        if (y&1) ans1=1ll*ans1*x%Mod;
        y>>=1;x=1ll*x*x%Mod;
    }
    return ans1;
}
signed main(){
    scanf("%lld%lld%lld%lld",&n,&a,&b,&k);
    scanf("%s",s);int opt=0;
    for (int i=0;i<k;i++){
        if (s[i]=='-') opt=-1;
        else opt=1;
        a1=1ll*(a1+1ll*opt*ksm(a,n-i)*ksm(b,i)%Mod)%Mod;
        a1=(a1+Mod)%Mod;
    }
    int xi=n+1;int num=xi/k;
    xi=xi-num*k;
    int tmpa=ksm(a,k*num);int tmpb=ksm(b,k*num);
    if (tmpa==tmpb){
        printf("%lld\n",1ll*a1*num%Mod);
        return 0;
    }
    int ans1=1ll*(1ll*a1*tmpa%Mod-1ll*a1*tmpb%Mod)%Mod;
    ans1=(ans1+Mod)%Mod;
    int fm=(tmpa-1ll*ksm(b,k)*ksm(a,k*num-k)%Mod)%Mod;
    fm=(fm+Mod)%Mod;
    ans1=1ll*ans1*ksm(fm,Mod-2)%Mod;
    printf("%lld\n",ans1%Mod);
    return 0;
}

8.CF917A

很无聊的题

代码如下:

#include<bits/stdc++.h>
using namespace std;
char s[500005];int ans=0;
int main(){
    scanf("%s",s+1);
    int n=strlen(s+1);
    for (int i=1;i<=n;i++){
        int zuo=0;int you=0;
        for (int j=i;j<=n;j++){
            if (s[j]=='(') zuo++,you++;
            if (s[j]==')') zuo--,you--;
            if (s[j]=='?') zuo--,you++;
            if (zuo<0&&you>0) zuo+=2;
            if (you<0) break;
            if (!zuo) ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}

9.CF917B

感觉已经相当套路了

sg好多都可以记忆化搜索

把所有状态遍历一遍,然后求mex

代码如下:

#include<bits/stdc++.h>
#define N 105
using namespace std;
int n,m,x,y,kk,head[N],dp[N][N][27];char ch;
struct Edge{int nxt,to;char ch;}e[N*N*2];
inline void link(int x,int y,char z){e[++kk].nxt=head[x];e[kk].to=y;e[kk].ch=z;head[x]=kk;}
bool dfs(int x,int y,int now){
    if (dp[x][y][now]!=-1) return dp[x][y][now];
    for (int i=head[x];i;i=e[i].nxt){
        int v=e[i].to;
        if (e[i].ch-'a'+1>=now&&dfs(y,v,e[i].ch-'a'+1)==0) return dp[x][y][now]=1;
    }
    return dp[x][y][now]=0;
}
int main(){
    scanf("%d%d",&n,&m);
    memset(dp,-1,sizeof(dp));
    for (int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        ch=getchar();
        while (ch<'a'||ch>'z') ch=getchar();
        link(x,y,ch);
    }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++) dp[i][j][0]=dfs(i,j,0);
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            if (dp[i][j][0]) putchar('A');
            else putchar('B');
        }puts("");
    }
    return 0;
}

 10.APIO2018 T3

显然构造圆方树

定义方点的大小为所在点双的大小

圆点大小为-1

dp一下即可

代码如下:

#include<bits/stdc++.h>
#define N 500005
#define int long long
using namespace std;
int n,m,x,y,kk,ans,cnt,tim1,sum,top,w[N],s[N],head[N],dfn[N],low[N],sz[N];
vector<int>G[N];
struct Edge{int nxt,to;}e[N];
inline void link(int x,int y){e[++kk].nxt=head[x];e[kk].to=y;head[x]=kk;}
void tarjan(int u,int fa){
    low[u]=dfn[u]=++tim1;s[++top]=u;
    for (int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if (v==fa) continue;
        if (!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]){
                ++cnt;w[cnt]=1;
                G[u].push_back(cnt);
                while (s[top]!=v) w[cnt]++,G[cnt].push_back(s[top--]);
                G[cnt].push_back(s[top--]);w[cnt]++;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}
void dfs(int u,int fa){
    sz[u]=(u<=n);
    for (int i=0;i<(int)G[u].size();i++){
        int v=G[u][i];
        if (v==fa) continue;
        dfs(v,u);
        sz[u]+=sz[v];
    }
}
void dp(int u,int fa){
    int now=u<=n;
    for (int i=0;i<(int)G[u].size();i++){
        int v=G[u][i];
        if (v==fa) continue;
        ans=ans+sz[v]*now*w[u];
        dp(v,u);
        now+=sz[v];
    }
    ans=ans+(sum-now)*now*w[u];
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for (int i=1;i<=m;i++){
        scanf("%lld%lld",&x,&y);
        link(x,y);link(y,x);
    }
    for (int i=1;i<=n;i++) w[i]=-1;
    cnt=n;
    for (int i=1;i<=n;i++)
        if (!dfn[i]){
            tarjan(i,-1);
            dfs(i,-1);
            sum=sz[i];
            dp(i,-1);
        }
    printf("%lld\n",ans*2);
    return 0;
}

 

posted @ 2018-09-13 21:02  longint  阅读(184)  评论(0编辑  收藏  举报