contest15

[比赛链接]https://vjudge.net/contest/168929#problem)

比赛征程

  • Y : solved in the contest
  • A : solved after contest
  • SY : solving
#ABCDE
contest0YYAAA

比赛中解决的问题

A

Problem description

给你四个正整数a,b,c,d,问你存不存在时刻,使得(a+k1 * b)与 (c+k2 * s d)相等?k1,k2为正整数。

Data Limit:a,b,c,d<=100  Time Limit: 1s

Solution

1、直接暴枚到30000000 O(30000000)*小常数
2、枚举k1到10000 O(10000)*小常数
3、扩展欧几里德算法

Code

using namespace std;
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
int x,y;
int gcd(int x,int y)
{
    return y==0?x:gcd(y,x%y);
}
void exgcd(int a,int b,int &x,int &y)
{
    if(b==0){x=1;y=0;return;} exgcd(b,a%b,x,y);int xx=x,yy=y;y=(xx-(a/b)*yy);x=yy;
}
int a,b,c,d;
int main()
{
    cin>>a>>b>>c>>d;
    int it=gcd(a,c);
    if(((d-b)/it)*it!=(d-b)){puts("-1");return 0;}
    exgcd(a,c,x,y);
    x*=(d-b)/it;
    y*=(d-b)/it;
//  cout<<x<<" "<<y<<endl;
    it=(a*c)/it;
    int t1=it/a,t2=it/c;
    while(y>0)
    {
        y-=t2;x+=t1;
    }
    while(abs(y)>t2)
    {
        y+=t2;x-=t1;
    }
    while(x<0) {x+=t1;y-=t2;}
    cout<<x*a+b<<endl;
}

B

Problem description

每个宇宙来两个人,这两个人一叛徒一忠臣,他们报了一些组,当某一组全为叛徒时,机会取消,问是否有这样的可能性。

Data Limit:n <= 1e4  Time Limit: 2s

Solution

直接模拟即可。每次每组若出现两个来自同一个平行宇宙的,说明该组没危险。

Code

    scanf("%d",&n);cin>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&num);
        memset(vis,0,sizeof(vis));
        bool flag=0;
        for(int j=1;j<=num;j++)
        {
            scanf("%d",&pp[j]);
        }
        for(int j=1;j<=num;j++) if(pp[j]>0) vis[pp[j]]=1;
        for(int j=1;j<=num;j++) if(pp[j]<0) if(vis[-pp[j]]){flag=1;break;}
        if(flag);else {puts("YES");return 0;}
    }
    puts("NO");

赛后补题

C

Problem description

两个人玩游戏,每个人有一个集合,两人轮流游戏,每次他们能让怪兽顺时针走他们选出的自己集合中的数步(可以多次使用),某人走完后怪兽到了结束点就赢了,问对于怪兽在每个位置(不在
游戏结束点)某人先手是否必胜/必输或有策略无限循环?

Data Limit:n <= 7e3  Time Limit: 4s

Solution

这种博弈问题看到题目就两种思路:
1、SG函数,这道题很难用
2、广搜的赢点输点DP,一方不能再走为输
对于一个赢点,那么所有对方能转移到他的点都为输点。
对于一个点,如果所有他能够出发到达的点都是对手的赢点,那么该点为输点
这是一大类套路DP,我要多做这方面的题。

Code

using namespace std;
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#define N 7001
struct NODE{
    int x,y,z;
}ind;
map<int,string>M;
int k[2],s[N][2];
int n,m;
bool b[N][3];
int ans[N][3];
int du[N][3];
queue<NODE>q;
void p(int x,int y,int z)
{
    if(b[x][y]) return;
    ind.x=x;ind.y=y;ind.z=z;
//  cout<<x<<" "<<y<<" "<<z<<endl;
    b[x][y]=1;q.push(ind);ans[x][y]=z;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<=n;i++) for(int j=0;j<2;j++) ans[i][j]=3;
    for(int i=0;i<2;i++)
    {
        scanf("%d",&k[i]);
        for(int j=1;j<=k[i];j++) scanf("%d",&s[j][i]); 
    }
    for(int i=0;i<2;i++)
    {
        for(int j=1;j<=n;j++)
        {
            du[j][i]=k[i];
        }
    }
    M[0]="Win";M[1]="Lose";M[3]="Loop";
    p(0,0,1);p(0,1,1);
    while(!q.empty())
    {
        NODE hh=q.front();q.pop();
        int x=hh.x,y=hh.y,z=hh.z;
        if(z)
        for(int i=1;i<=k[y^1];i++) p((x-s[i][y^1]+n*5)%n,y^1,0);
        else
        {
            for(int i=1;i<=k[y^1];i++)
            {
                du[(x-s[i][y^1]+n*5)%n][y^1]--;
                if(!du[(x-s[i][y^1]+n*5)%n][y^1])
                p((x-s[i][y^1]+n*5)%n,y^1,1);
            }
        }
    }
    for(int i=0;i<2;i++)
    {
        for(int j=1;j<=n-1;j++)
        {
            cout<<M[ans[j][i]]<<" ";
        }
        puts("");
    }
}

D

Problem description

有三种类型的道路,1.花费x,从u->v.
2、花费x,从u->[L,R]中任意一点。
3、花费x,从[L,R]中任意一点->u;
求s,到所有点的最少花费

Data Limit:n <= 1e5  Time Limit: 2s

Solution

这题是虚拟建建节点的方法,利用类似线段树的构造,把边数优化到nlogn
这种题用单项边汇。

Code

using namespace std;
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define r(x) scanf("%lld",&x)
#define forw(i,x) for(long long i=fir[x];i;i=ne[i])
#define N 3000001
long long root1,n,m,root2;
long long ty,x,u,v,l,r,w;
long long ne[N],dis[N],fir[N],to[N],C[N],cnt,tot;
bool inq[N];
long long s;
void add(long long x,long long y,long long z)
{
    ne[++cnt]=fir[x];fir[x]=cnt;to[cnt]=y;C[cnt]=z;
}
long long L[N],R[N];
long long lc[N],rc[N],sz;
queue<long long>q;
long long build1(long long l,long long r)
{
    if(l==r) {L[l]=l;R[l]=r;return l;}
    ++sz;
    long long mid=(l+r)>>1;
    long long no=sz;
    add(no,lc[no]=build1(l,mid),0);add(no,rc[no]=build1(mid+1,r),0);
    L[no]=l;R[no]=r;
    return no;
}
long long build2(long long l,long long r)
{
    if(l==r) {L[l]=l;R[l]=r;return l;}
    ++sz;long long mid=(l+r)>>1;
    long long no=sz;
    add(lc[no]=build2(l,mid),no,0);add(rc[no]=build2(mid+1,r),no,0);
    L[no]=l;R[no]=r;
    return no;
}
void ADD1(long long no,long long u,long long l,long long r,long long w)
{
    if(L[no]==l&&R[no]==r) {add(u,no,w);return;}
    long long mid=(L[no]+R[no])>>1;
    if(l>mid)
    ADD1(rc[no],u,l,r,w);else
    if(r<=mid)
    ADD1(lc[no],u,l,r,w);else
    {
        ADD1(lc[no],u,l,mid,w);
        ADD1(rc[no],u,mid+1,r,w);
        return;
    }
}
void ADD2(long long no,long long u,long long l,long long r,long long w)
{
    if(L[no]==l&&R[no]==r) {add(no,u,w);return;}
    long long mid=(L[no]+R[no])>>1;
    if(l>mid)
    ADD2(rc[no],u,l,r,w);else
    if(r<=mid)
    ADD2(lc[no],u,l,r,w);else
    {
        ADD2(lc[no],u,l,mid,w);
        ADD2(rc[no],u,mid+1,r,w);
        return;
    }
}
void SPFA(long long s)
{
    q.push(s);inq[s]=1;dis[s]=0;
    while(!q.empty())
    {
        long long ind=q.front();q.pop();inq[ind]=0;
        forw(i,ind)
        {
            long long V=to[i];
            if(dis[V]>dis[ind]+C[i])
            {
                dis[V]=dis[ind]+C[i];
                if(!inq[V])
                {
                    inq[V]=1;
                    q.push(V);
                }
            }
        }
    }
}
main()
{
    cin>>n>>m>>s;sz=n;
    root1=build1(1,n);
    root2=build2(1,n);
    for(long long i=1;i<=m;i++)
    {
        cin>>ty;
        if(ty==1) 
        {
            r(u);r(v);r(w);add(u,v,w);
        }
        if(ty==2) 
        {
            r(v);r(l);r(r);r(w);
            ADD1(root1,v,l,r,w);
        }
        if(ty==3)
        {
            r(v);r(l);r(r);r(w);
            ADD2(root2,v,l,r,w);
        }
    }
    for(long long i=1;i<=sz;i++) dis[i]=1e18;
    SPFA(s);
    for(long long i=1;i<=n;i++)
    {
        if(dis[i]<1e18) cout<<dis[i]<<' ';else cout<<"-1 ";
    }
}

E

Problem description

一个有n个颜色的排列,对于特定的k,求将原序列分成最少的连续的段数,使每段的颜色数小于等于k。输出k=1~n的答案。

Data Limit:n <= 1e5  Time Limit: 2s

Solution

1、如果k=l的个数等于k=r的个数那么k=l~r这么多数答案均相同,这样就每次二分+模拟就可以了。调和级数约为logn.所以O(nlog^2n)

2、这类区间的题,可以用切割区间然后按左端点排序从左到右查找更新的方法解决,用树状数组维护从i~j内的不同颜色数,然后二分最远位置即可。
特殊处理就是nxt数组记录下一个这种颜色,以便查询完i后删除i。很好的方法,对离线问题值得借鉴!

##Code1

using namespace std;
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 200001
int n,color[100001];
int ans[N];
int confuse[100001];
bool num[100001];
int cal(int x)
{
    if(confuse[x]) return confuse[x];
    for(int l=1;l<=n;)
    {
        int cnt=0;
        int f=l;
        while(cnt<x&&f<=n)
        {
            if(!num[color[f]])cnt++;
            num[color[f++]]=1;
        }
        while(num[color[f]]&&f<=n)f++;
        if(!num[color[f]])f--;
        for(int k=l;k<=f;k++) num[color[k]]=0;
        confuse[x]++;l=f+1;
    }
    return confuse[x];
}
void doit(int l,int r)
{
    if(l>r) return;
    int rr=r,ll=l+1;
    int an=l;
    while(ll<=rr)
    {
        int mid=(ll+rr)>>1;
        if(cal(mid)==cal(l))
        {
            ll=mid+1;an=mid;
        }
        else
        rr=mid-1;
    }
    for(int i=l;i<=an;i++) 
    {
        ans[i]=cal(l);
    }
    doit(an+1,r);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&color[i]);
    doit(1,n);
    for(int i=1;i<=n;i++)
    {
        cout<<ans[i]<<" ";
    }
}

##Code1

using namespace std;
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 300001
#define F(i,a,b) for(int i=a;i<=b;i++)
#define lowbit(x) (x&(-x))
int c[N],S[N];
int n,m;
vector<int>q[N];
int col[N];
void add(int x,int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
    S[i]+=v;
}
int get(int k)
{
    int res=0;
    for(int i=16;i>=0;i--)
    {
        int x=(1<<i);
        if(res+x<=n)
        if(S[res+x]<=k)
        {
            res+=x;
            k-=S[res];
        }
    }
    return res;
}
int num[N];
int ans[N];
int nxt[N];
int main()
{
    scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=n;i++)
    {
        q[1].push_back(i);
        if(!num[c[i]])
        {
            num[c[i]]=i;
            add(i,1);
        }
        else 
        {
            nxt[num[c[i]]]=i;
            num[c[i]]=i;
        }
    }
    F(i,1,n)
    {
        int hh=q[i].size();
        for(int j=0;j<hh;j++)
        {
            int xx=get(q[i][j]);
            ans[q[i][j]]++;
            q[xx+1].push_back(q[i][j]);
        } 
        hh=nxt[i];
        add(i,-1);if(hh>0) add(hh,1);
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值