UESTC 2019 Summer Training #1 Div.2

菜鸡不配拥有姓名
A A A
题意:每次可以将一个大正方形切成四个小正方形,并且保证切割之后从左下角到右上角走过的正方形都是同样大小的。
参考:https://blog.csdn.net/Tiw_Air_Op1721/article/details/84454136
这个题解写的比较详细。
主要的意思是:
我们最终要实现的形态是这样的:
在这里插入图片描述
最左和左上的正方形不断切割保证同时切割,剩下的可以随意切割。
显然对于每个状态,剩下的不继续切割则是最小切割次数,全部切割则是最大切割次数。

对于一个正方形的最大切割次数,显然就是 f ( n ) = f ( n − 1 ) ∗ 4 f(n)=f(n-1)*4 f(n)=f(n1)4的求和。
显然是不只一个正方形的,实质上在每次状态变化的时候,都会多出此时外圈边长大小的正方形,他们是属于可以继续切割的。
比如这张图里一共有 5 5 5个。
在这里插入图片描述
再看这张图,对于刚刚的那 5 5 5个,除了左上角的那个对应了 5 5 5个小的,其他都对应 2 2 2个。
所以每次增加的小正方形个数为 2 ∗ ( t m p − 1 ) + 5 2*(tmp-1)+5 2(tmp1)+5
不断记录总和即可。

对于最小切割次数,状态为 n − 1 n-1 n1显然为1次,状态为 n − 2 n-2 n2则是3次。对于第 i i i次切割次数, G [ i ] G[i] G[i]肯定能切割出 2 ∗ ( G [ i ] − 1 ) + 3 2*(G[i]-1)+3 2(G[i]1)+3个小的,也就是第 i + 1 i+1 i+1次切割次数。
大胆猜想:公式为 2 i − 1 2^i-1 2i1,实际直接递推也可以。

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
#define ll long long
#define ULL unsigned long long
using namespace __gnu_pbds;

const ll mod=1e9+7;

ll cal(int x){
    ll tmp=1,tot=0;
    while(x){
        tot+=tmp;
        tmp*=4;
        x--;
    }
    return tot;
}

void slove(){
    ll n,k;cin>>n>>k;
    if(n>=32){
        printf("YES %d\n",n-1);
        return ;
    }
    ll tot=0,tmp=1,mx=0;
    for(int i=n-1;i>=0;i--){
        tot+=(1ll<<(n-i))-1;
        mx+=tmp*cal(i);
        tmp=2*(tmp-1)+5;
        if(k>=tot&&k<=mx+tot){
            printf("YES %d\n",i);
            return ;
        }
        if(tot>k)break;
    }
    printf("NO\n");
}

int main(){
    int t;cin>>t;
    FOR(i,1,t)slove();
}

B B B 未 做 {未做}
C C C
二分+哈希
D D D
贪心+二分。
确实好题。
题意:给你 n n n段时间区间,如果某个时间点被 k k k个区间包含,就需要 k k k个电视。
费用是电视费用 x + x+ x+租用时间 ∗ y *y y

题解:我们排序时间区间,最后使得结果处理是按序的。
按序处理怎么解决呢?
对于每个区间有两个选择,放到已有电视或者开个新电视,放到已有电视放在已有电视最近结束时间的即可。我们可以加入 m u l t i s e t multiset multiset中进行二分查找(放入结束时间。
这样做合法吗?我们每次加新电视显然不会对后面有影响,有影响的加到已有电视上。
|| ||
|
_______| |___|
对于区间,右下先匹配最后一个,右上就得匹配左上。
如果反过来的话结果其实是一样的。
所以我们选择更好处理的方式。

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
#define ll long long
#define ULL unsigned long long
using namespace __gnu_pbds;

const ll mod=1e9+7;

struct node{
    ll l,r;
    friend bool operator < (node a,node b){
        if(a.l!=b.l)return a.l<b.l;
        return a.r<b.r;
    }
}A[500050];

multiset<int>mt;

int main(){
    int n,x,y;
    cin>>n>>x>>y;
    ll ans=0;
    FOR(i,1,n){
        ll a,b;
        scanf("%lld%lld",&a,&b);
        A[i]=node{a,b};
        ans=(ans+y*(b-a)+mod)%mod;
    }
    sort(A+1,A+1+n);
    //cout<<ans<<endl;
    FOR(i,1,n){
        ll tmp1=0,tmp2=x;
        auto it=mt.lower_bound(A[i].l);
        if(it==mt.begin())tmp1=0x3f3f3f3f;
        else{
            --it;
            tmp1=y*(A[i].l-*it);
        }
        if(tmp1<tmp2){
            mt.erase(it);
            ans=(ans+tmp1)%mod;
        }
        else ans=(ans+tmp2)%mod;
        mt.insert(A[i].r);
        //cout<<ans<<endl;
    }
    printf("%lld\n",ans);
}

E E E
不可做
F F F
未做
G G G
不可做
H H H
水题
I I I
题意:求 k k k能组成的最大凸包周长,周长等于相邻两点曼哈顿距离之和。
题解: 显然对于 k ≥ 4 k≥4 k4的点,周长等价于 2 ∗ ( 最 上 − 最 下 + 最 右 − 最 左 ) 2*(最上-最下+最右-最左) 2(+)
因为一定形成凸包,简单画图即可发现上面的公式。
但是对于三角形比较特殊。
但是每个点必然有个点是三角形中的,遍历每个点,对于个点,往外延伸即可以得到最大周长。显然延伸到边界最大,相邻边界一共有 4 4 4个情况,相对边界有 2 2 2个。但对于相对边界,画图可以发现,这种情况可以转换成相邻的。
在这里插入图片描述
对于圆点,显然后者更优,但是后者可以转换成对于下面的 X X X的相邻边界。

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
#define ll long long
#define ULL unsigned long long
using namespace __gnu_pbds;
 
ll ml=0x3f3f3f3f,mr=-0x3f3f3f3f,mu=-0x3f3f3f3f,md=0x3f3f3f3f;
ll A[300050][2];
int n;
ll ans1=-1,ans2=-1;
 
int main(){
    cin>>n;
    FOR(i,1,n){
        scanf("%lld%lld",&A[i][0],&A[i][1]);
        ml=min(ml,A[i][0]);
        mr=max(mr,A[i][0]);
        mu=max(mu,A[i][1]);
        md=min(md,A[i][1]);
    }
   // cout<<ml<<" "<<mr<<" "<<mu<<" "<<md<<endl;
    ans2=2*(mr-ml)+2*(mu-md);
    FOR(i,1,n){
        ans1=max(ans1,2*abs(A[i][0]-ml)+2*abs(A[i][1]-mu));
        ans1=max(ans1,2*abs(A[i][0]-ml)+2*abs(A[i][1]-md));
        ans1=max(ans1,2*abs(A[i][0]-mr)+2*abs(A[i][1]-mu));
        ans1=max(ans1,2*abs(A[i][0]-mr)+2*abs(A[i][1]-md));
    }
    printf("%lld ",ans1);
    for(int i=4;i<=n;i++){
        printf("%lld ",ans2);
    }
}

J J J
题意:
这种类似的问题显然是带权并查集。给你区间异或值,求解区间异或值。
考虑合法性:
对于(a ^ b ^ c) ^ *( b ^ c)=a。结果就很显然了。
类似于区间和的方式,用异或得到区间和,每个点存储到自己的父亲的异或值。
f i n d find find的时候,不断异或的到区间异或值。
我们只维护区间异或前缀值。 ( x − 1 , y ] = [ x , y ] (x-1,y]=[x,y] (x1,y]=[x,y]

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
#define ll long long
#define ULL unsigned long long
using namespace __gnu_pbds;

int n;
map<int,int>mp,sum;

int find(int x){
    if(mp[x]==0||mp[x]==x)return x;
    else{
        int t=mp[x];
        mp[x]=find(mp[x]);
        sum[x]^=sum[t];
        return mp[x];
    }
}

void uni(int x,int y,int s){
    x++,y+=2;
    int px=find(x);
    int py=find(y);
    if(px!=py){
        mp[py]=px;
        sum[py]=s^sum[y]^sum[x];
    }
}

int query(int x,int y){
    x++,y+=2;
    //cout<<find(x)<<" "<<find(y)<<endl;
    if(find(x)==find(y))return sum[y]^sum[x];
    else return -1;
    //cout<<find(x)<<"??"<<find(y)<<endl;
}

int main(){
    cin>>n;
    int last=0;
    FOR(i,1,n){
        int t,x,y,data;
        scanf("%d%d%d",&t,&x,&y);
        x^=last,y^=last;
        if(x>y)swap(x,y);
        if(t==1){
            scanf("%d",&data);
            data^=last;
      //      cout<<x<<"->"<<y<<"->"<<data<<endl;
            uni(x,y,data);
        }
        else{
            int tmp=query(x,y);
            printf("%d\n",tmp);
            if(tmp==-1)last=1;
            else last=tmp;
        }
    }
    //cout<<sum[1]<<" "<<sum[2]<<" "<<sum[3]<<" "<<sum[4]<<endl;
}

K 、 L K、L KL
水题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值