Gym.101908 Brazil Subregional Programming Contest(寒假自训第六场)

这几天失眠时间都不太够,室友太会折腾了,感觉有点累,所以昨天的题解也没写,看晚上能不能补起来。

B . Marbles

题意:给定N组数(xi,yi),玩家轮流操作,每次玩家可以选择其中一组对其操作,可以把它减去一个数,或同时减去一个数,当玩家操作后出现了(0,0)则胜利。

思路:注意这里是出现(0,0)胜,而不是全都是(0,0)胜,所以我们不能简单的球sg,最后异或得到答案。

但是我们转化一下,如果玩家面对的全是(1,2) 或(2,1),则他胜利,那么我们可以以这两个状态为起点得到sg函数,就转化为了nim博弈。 当然,前提是没有xi==yi的情况。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=210;
int sg[maxn][maxn],vis[maxn];
void getsg()
{
    rep(i,1,100)
     rep(j,1,100){
         if(i==j) continue;
         memset(vis,0,sizeof(vis));
         rep(k,1,min(i-1,j-1)) vis[sg[i-k][j-k]]=1;
         rep(k,1,i-1) if(i-k!=j) vis[sg[i-k][j]]=1;
         rep(k,1,j-1) if(i!=j-k) vis[sg[i][j-k]]=1;
         rep(k,0,10000) if(!vis[k]){ sg[i][j]=k; break; }
    }
}
int main()
{
    int N,F=0,x,y,sum=0;
    getsg();
    scanf("%d",&N);
    rep(i,1,N){
        scanf("%d%d",&x,&y);
        sum^=sg[x][y];
        if(x==y) F=1;
    }
    if(F||sum) puts("Y");
    else puts("N");
    return 0;
}

C .Pizza Cutter

题意:对于一个匹萨,限制横着切N刀,竖着切M刀,问最后披萨被分为了多少块。 保证不存在超过三刀相交在一点的情况,以及在角上相交的情况。

思路:我们发现,一刀的贡献是与它相交的直线数+1。 横线和竖线的交点=N*M。 横线和横线的交点=逆序对数。竖线与竖线一样。

所以求两次逆序对即可。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
struct in{
    int x,y;
    bool friend operator <(in w,in v) { return w.x<v.x;}
}s[maxn];
int sum[maxn],b[maxn],tot;
void add(int a,int N){
    for(int i=a;i<=N;i+=(-i)&i) sum[i]++;
}
int query(int a){
    int res=0; for(int i=a;i;i-=(-i)&i) res+=sum[i];
    return res;
}
ll get(int N)
{
    sort(s+1,s+N+1); ll res=0;
    rep(i,1,N) sum[i]=0,b[i]=s[i].y;
    sort(b+1,b+N+1); tot=unique(b+1,b+N+1)-(b+1);
    rep(i,1,N) s[i].y=lower_bound(b+1,b+N+1,s[i].y)-b;
    for(int i=N;i>=1;i--){
        res+=query(s[i].y);
        add(s[i].y,N);
    }return res;
}
int main()
{
    int N,M; ll ans=0;
    scanf("%d%d",&N,&M); scanf("%d%d",&N,&M);
    rep(i,1,N) scanf("%d%d",&s[i].x,&s[i].y);
    ans=get(N);
    rep(i,1,M) scanf("%d%d",&s[i].x,&s[i].y);
    ans+=get(M);
    ans+=(ll)N*M+N+M+1;
    printf("%lld\n",ans);
    return 0;
}

D .Unraveling Monty Hall

题意:统计不是1的个数。

思路:水题,就是题面太长。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
int main()
{
    int N,x,ans=0;
    scanf("%d",&N);
    rep(i,1,N) scanf("%d",&x),ans+=(x!=1);
    printf("%d\n",ans);
    return 0;
}

E .Enigma

题意:给定字符串S,T,让T去匹配S,问有多少个匹配没有相同的字符。

思路:模拟。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1000010;
char c[maxn],b[maxn];
int main()
{
    int N,M,ans=0;
    scanf("%s%s",c+1,b+1);
    N=strlen(c+1); M=strlen(b+1);
    rep(i,1,N-M+1) {
        bool F=true;
        rep(j,1,M) if(b[j]==c[i+j-1]) F=false;
        ans+=(F);
    }
    printf("%d\n",ans);
    return 0;
}

F .Music Festival

题意:有N个舞台(N<10),第i个舞台有Mi首歌(M总和<1000),给出每首歌的起始时间s,和终止时间t,以及价值val; 我们听完一首歌可以任意瞬移到另外的舞台,即不考虑时间边界。现在让你选择一种方案,使得每个舞台都至少听了一首歌,求最大价值。

思路:区间最值更新答案的,我们可以用线段树或者单调队列更新答案。这里用了线段树。 先离散化时间(2*N个),然后对于所有的节目,按起始时间排序(终止时间排序效果也一样),然后每次查询[1,s]的最大值,然后用结果去更新t点的值。 复杂度O(N^2*logN)。记录一个前缀最大值复杂度是O(N^2)的。

线段树:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep2(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int maxn=1024;
int dp[maxn][maxn],b[maxn<<1],cnt; bitset<maxn>S[maxn];
struct in{
    int s,t,val,id;
    bool friend operator <(in w,in v){
        return w.s<v.s;
    }
}s[maxn];
struct Tree{
    int Mx[maxn<<3];
    void update(int Now,int L,int R,int pos,int val)
    {
        Mx[Now]=max(Mx[Now],val);
        if(L==R) return ; int Mid=(L+R)>>1;
        if(pos<=Mid) update(Now<<1,L,Mid,pos,val);
        if(pos>Mid) update(Now<<1|1,Mid+1,R,pos,val);
    }
    int query(int Now,int L,int R,int l,int r)
    {
        if(l<=L&&r>=R) return Mx[Now];
        int Mid=(L+R)>>1,res=0;
        if(l<=Mid) res=max(res,query(Now<<1,L,Mid,l,r));
        if(r>Mid) res=max(res,query(Now<<1|1,Mid+1,R,l,r));
        return res;
    }
}T[1024];
int main()
{
    int N,M,tot=0,ans=0;
    scanf("%d",&N);
    rep(i,0,N-1){
        scanf("%d",&M);
        rep(j,1,M){
            tot++;
            scanf("%d%d%d",&s[tot].s,&s[tot].t,&s[tot].val);
            s[tot].id=i; b[++cnt]=s[tot].s; b[++cnt]=s[tot].t;
        }
    }
    sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-(b+1);
    sort(s+1,s+tot+1); M=(1<<N)-1;
    rep(i,1,tot) s[i].s=lower_bound(b+1,b+cnt+1,s[i].s)-b;
    rep(i,1,tot) s[i].t=lower_bound(b+1,b+cnt+1,s[i].t)-b;
    rep(i,1,tot) {
        dp[i][1<<s[i].id]=s[i].val;
        rep(j,1,M){
            if(T[j].Mx[1]==0) continue;
            int Mx=T[j].query(1,1,cnt,1,s[i].s);
            if(Mx!=0) dp[i][j|(1<<s[i].id)]=max(dp[i][j|(1<<s[i].id)],Mx+s[i].val);
        }
        rep(j,1,M) {
            if(dp[i][j]) T[j].update(1,1,cnt,s[i].t,dp[i][j]);
        }
        ans=max(ans,dp[i][M]);
    }
    if(ans==0) ans=-1;
    printf("%d\n",ans);
    return 0;
}

 前缀最大值: (数组开小wa了一发)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep2(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int maxn=1024;
int dp[maxn][maxn],pre[maxn][maxn<<1],b[maxn<<1],cnt;
struct in{
    int s,t,val,id;
    bool friend operator <(in w,in v){
        return w.s<v.s;
    }
}s[maxn];
int main()
{
    int N,M,tot=0,ans=0;
    scanf("%d",&N);
    rep(i,0,N-1){
        scanf("%d",&M);
        rep(j,1,M){
            tot++;
            scanf("%d%d%d",&s[tot].s,&s[tot].t,&s[tot].val);
            s[tot].id=i; b[++cnt]=s[tot].s; b[++cnt]=s[tot].t;
        }
    }
    sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-(b+1);
    sort(s+1,s+tot+1); M=(1<<N)-1;
    rep(i,1,tot) s[i].s=lower_bound(b+1,b+cnt+1,s[i].s)-b;
    rep(i,1,tot) s[i].t=lower_bound(b+1,b+cnt+1,s[i].t)-b;
    int pos=1;
    rep(i,1,tot) {
        while(pos<=s[i].s){ //更新前缀最大值
            rep(j,1,M) pre[j][pos]=max(pre[j][pos-1],pre[j][pos]);
            pos++;
        }
        int t=1<<s[i].id; dp[i][t]=s[i].val;
        pre[t][s[i].t]=max(pre[t][s[i].t],s[i].val);
        rep(j,1,M)
          if(pre[j][s[i].s])
            dp[i][j|t]=max(dp[i][j|t],pre[j][s[i].s]+s[i].val);
        rep(j,1,M)
          if(dp[i][j]) pre[j][s[i].t]=max(pre[j][s[i].t],dp[i][j]);
        ans=max(ans,dp[i][M]);
    }
    if(ans==0) ans=-1;
    printf("%d\n",ans);
    return 0;
}

-----吃饭ing----

原文地址:https://www.cnblogs.com/hua-dong/p/10350608.html

时间: 2024-08-01 11:17:47

Gym.101908 Brazil Subregional Programming Contest(寒假自训第六场)的相关文章

(寒假GYM开黑)2018-2019 ACM-ICPC Brazil Subregional Programming Contest

layout: post title: 2018-2019 ACM-ICPC Brazil Subregional Programming Contest author: "luowentaoaa" catalog: true tags: mathjax: true - codeforces 传送门 付队! 许老师! B.Marbles (nim博弈) 题意 一个棋盘n个点,每次可以把一个棋子移动到(x-d,y) or (x,y-d) or (x-d,y-d) 把其中一个棋子移动到(0

gym101908 [2018-2019 ACM-ICPC Brazil Subregional Programming Contest] 题解

感觉难度和邀请赛类似,题目质量也低于国内邀请赛,(题面/数据不出锅的情况下) https://codeforces.com/gym/101908 A.大概是莫比乌斯之类的,不会 B:博弈,不会 C:欧拉公式+二维偏序 首先,根据平面图欧拉公式,可推导出答案为$n+m+1+$交叉的数量 交叉的数量由三部分构成,横竖交叉数,横横交叉数和竖竖交叉数 显然,前者为$nm$,后两个为简单的二维偏序问题, 比较扯淡的是,先T了一发,从map改完二分,交的时候才意识到是没关同步 #include<bits/s

2018-2019 ACM-ICPC Brazil Subregional Programming Contest PART (10/13)

\[2018-2019 ACM-ICPC Brazil Subregional Programming Contest\] \(A.Slackline\ Adventure\) \(B.Marbles\) NIM游戏,把其中任意一个石子移动到(0,0)算赢,所以必败态为全部石子都到(1,2)和(2,1)这两个点而不是(0,0)了,跑出sg函数异或即可,注意如果出现先手直接赢的需要特判 //#pragma comment(linker, "/STACK:1024000000,1024000000&

2019-2020 ACM-ICPC Brazil Subregional Programming Contest (7/10)

\(2019-2020\ ACM-ICPC\ Brazil\ Subregional\ Programming\ Contest\) \(A.Artwork\) 并查集,把检测区域能在一起的检测器放在一个并查集里,然后判断是否有一个集合能够封住左边和上边的其中一个还有右边和下边的其中一个即可 //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using name

2018-2019 ACM-ICPC Brazil Subregional Programming Contest

A:留坑 B:二维sg函数,特判边界情况 //#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pragma GCC optimize(4) //#pragma GCC optimize("unroll-loops") //#pragma comment(linker, "/stack:200000000") //#pragma GCC optimize("Ofast,no-stack-protect

Gym102040 .Asia Dhaka Regional Contest(寒假自训第9场)

B .Counting Inversion 题意:给定L,R,求这个区间的逆序对数之和.(L,R<1e15) 思路:一看这个范围就知道是数位DP. 只是维护的东西稍微多一点,需要记录后面的各种数字的个数cnt,以及逆序对和sum,以及出现了多少种后缀num. 那么枚举到当前位时,假设为i ,那么sum+=cnt[i+1]+cnt[i+2]+....cnt[9];  cnt[i]+=num; 可以参考CF1073E. #include<bits/stdc++.h> #define rep(

Gym .101933 Nordic Collegiate Programming Contest (NCPC 2018) (寒假gym自训第四场)

(本套题算是比较温和吧,就是罚时有点高. B .Baby Bites 题意:给出一个婴儿给出的数组,有一些数字听不清楚,让你还原,问它是否是一个从1开始的一次增加的数组. 思路:从左往右依次固定,看是否有矛盾即可. #include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) #define rep2(i,a,b) for(int i=a;i>=b;i--) using n

Gym -102007 :Benelux Algorithm Programming Contest (BAPC 18) (寒假自训第5场)

A .A Prize No One Can Win 题意:给定N,S,你要从N个数中选最多是数,使得任意两个之和不大于S. 思路:排序,然后贪心的选即可. #include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; ll a[maxn]; int main() { int N,ans; l

Gym .101879 USP Try-outs (寒假自训第七场)

B .Aesthetics in poetry 题意:给定N个数,(N<2000 ,a[i] <=1e9),让你找一个最大的K,使得N个数膜K的余数个数全都等于N/K个. 思路:我们找到N的因子,然后验证即可,复杂度O(N^2) #include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; int a[maxn],N,a