落实1
落实1
这应该是我落实得最快的一次了。
T1 平板涂色
这道题我写的有点久,看到这种矩形就烦。但最后还是想到了一种神奇的做法:化矩形为图,我们能不能直接用图上的关系来表示这些矩形的关系呢?显然是可以的。怎么存呢?
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
if(node[i].xl==node[j].xr&&!(node[i].yr<node[j].yl||node[i].yl>node[j].yr)) adde(j,i);
}
}
这么存。把他上面的直接相连的相接触的矩形连一条边到下面这个矩形。
注意,别把 i!=j 写在 for 循环语句上,我也不知道为什么会错。。。
存了边后,我一看数据范围!原来是暴搜可以过的范围!好家伙,直接上暴搜。
我们先从当前入度为 \(0\) 的点的颜色开始消,用拓扑每次消掉颜色相同的矩形,换一次颜色就加一次更换颜色次数,最后加上一个小小的剪枝就可以通过此题了!
我觉得我这个搜索有点丑,但是还是 \(O(n^3)\) 的算法。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=25;
int n,start,ans=0x3f3f3f3f;
struct lzz{
int xl,yl,xr,yr;
int color;
}node[N];
int hd[N],to[N],nx[N],in[N],tot;
int vis[N];
void adde(int u,int v){
nx[++tot]=hd[u];to[tot]=v;in[v]++;hd[u]=tot;
}
void dfs(int num,int cnt){
if(num>ans) return;
if(cnt==n){
ans=min(ans,num);
return;
}
queue<int> q;
int color,sum=0;
int tmpvis[N],tmpin[N];
//copy
for(int i=1;i<=n;i++) tmpvis[i]=vis[i],tmpin[i]=in[i];
//doit
for(int i=1;i<=n;i++){
if(!in[i]&&!vis[i]){
color=node[i].color;
for(int j=1;j<=n;j++)
if(!in[j]&&!vis[j]&&node[j].color==color) sum++,q.push(j);
while(!q.empty()){
int u=q.front();q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int k=hd[u];k;k=nx[k]){
int v=to[k];
in[v]--;
if(!vis[v]&&node[v].color==color&&!in[v]){
q.push(v);
sum++;
}
}
}
dfs(num+1,cnt+sum);
for(int z=1;z<=n;z++) vis[z]=tmpvis[z],in[z]=tmpin[z];
sum=0;
}
}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
node[i].xl=read(),
node[i].yl=read(),
node[i].xr=read(),
node[i].yr=read(),
node[i].color=read();
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
if(node[i].xl==node[j].xr&&!(node[i].yr<node[j].yl||node[i].yl>node[j].yr))
adde(j,i);
}
}
dfs(0,0);
printf("%d",ans);
return 0;
}
总结:这题送分的,结果建图白学,建了个假图,还过了一半的点。。。
T2 火柴排队
这题就是 \(zfz\) 大佬秀操作的一题了,思路特别巧妙,具体证明以后再补(懒
就是求一波逆序对的事情。
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=1e5+5,MOD=1e8-3;
int n,ans;
struct lzz{
int id,high,rank;
}b[N],a[N];
//treearray
int tre[N];
void upd(int x,int v){
while(x<=n){
tre[x]+=v;
x+=lowbit(x);
}
}
int ask(int x){
int res=0;
while(x){
res+=tre[x];
x-=lowbit(x);
}
return res;
}
bool cmp1(lzz x,lzz y){return x.high<y.high;}
bool cmp2(lzz x,lzz y){return x.id<y.id;}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i].high=read(),a[i].id=i;
for(int i=1;i<=n;i++) b[i].high=read(),b[i].id=i;
sort(b+1,b+1+n,cmp1);
sort(a+1,a+1+n,cmp1);
for(int i=1;i<=n;i++) a[i].rank=b[i].id;
sort(a+1,a+1+n,cmp2);
for(int i=1;i<=n;i++){
upd(a[i].rank,1);
(ans+=i-ask(a[i].rank))%=MOD;
}
printf("%d",ans);
return 0;
}
总结:不会从简单的式子入手找出规律 =-=
T3 线性函数
这题就是线段树,我考场也看出来了,但是实现了一半突然感觉不能实现,就白给了。其实这就是一道码力题,只要你线段树打的优美就可以过了。
其实很好实现,对于每个函数:\(f(x)=kx+b\),我们存一个 segtreek 和一个 segtreeb,来表示。
那么修改只需要线段树的单点修改即可。
我们主要来考虑怎么合并,对于线段树上的父节点和他的两个子节点,我们只需要先处理左儿子,然后再用所得的值来处理右儿子,即可满足题目的从右到左嵌套的要求。说白了,就是先序遍历,就不需要做什么改动,代码简洁明了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls x<<1
#define rs x<<1|1
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=2e5+5,MOD=1e9+7;
int n,Q;
int k[N],b[N];
int segtreek[N<<2+5],segtreeb[N<<2+5];
struct node{
int k,b;
};
void build(int x,int l,int r){
if(l==r){
segtreek[x]=k[l];
segtreeb[x]=b[l];
return;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
segtreek[x]=1ll*segtreek[ls]*segtreek[rs]%MOD;
segtreeb[x]=(1ll*segtreek[rs]*segtreeb[ls]%MOD+segtreeb[rs])%MOD;
}
void update(int pos,int x,int l,int r,int vk,int vb){
if(l==r){
segtreek[x]=vk;
segtreeb[x]=vb;
return;
}
int mid=l+r>>1;
if(pos<=mid) update(pos,ls,l,mid,vk,vb);
else update(pos,rs,mid+1,r,vk,vb);
segtreek[x]=1ll*segtreek[ls]*segtreek[rs]%MOD;
segtreeb[x]=(1ll*segtreek[rs]*segtreeb[ls]%MOD+segtreeb[rs])%MOD;
}
node query(int x,int l,int r,int xl,int xr){
if(xl<=l&&xr>=r) return (node){segtreek[x],segtreeb[x]};
int mid=l+r>>1;
node res={1,0};
if(xl<=mid) res=query(ls,l,mid,xl,xr);
if(xr>mid){
node tmp=query(rs,mid+1,r,xl,xr);
int kk=tmp.k*res.k%MOD,bb=(tmp.k*res.b%MOD+tmp.b)%MOD;
res=(node){kk,bb};
}
return res;
}
signed main(){
n=read(),Q=read();
for(int i=1;i<=n;i++) k[i]=read(),b[i]=read();
build(1,1,n);
char opt;
int x,y,z;
while(Q--){
opt=getchar();
x=read(),y=read(),z=read();
if(opt=='M') update(x,1,1,n,y,z);
else{
node tmp=query(1,1,n,x,y);
printf("%d\n",(1ll*tmp.k*z%MOD+tmp.b)%MOD);
}
}
return 0;
}
T4 超级钢琴
RMQ神题,思路牛的爆炸。
要求一段区间的和,首先想到前缀和,要求区间 \([i,j] (j \in [i+L-1,i+R-1])\) 的和最大的区间,很显然,假设前缀和为 sum 数组,最大值就是 \(sum[j]-sum[i-1]\),因为对于每个以 \(i\) 开头的区间,\(sum[i-1]\) 都是确定的,所以我们只要找出最大的 \(sum[j]\) 就好了。怎么找呢?区间最大值,想到好写的st表。找出最大值后,我们把他们放进大根堆里面,我们的目的就变为了从堆顶取出 \(k\) 个元素。对于任意一个堆顶的元素,取出他后,以他的 \(i\) 为开头的区间还可能有最大值产生,所以我们要在 \(j\) 的两边再用st表找两个点放进堆里面。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int ans=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
return ans*f;
}
const int N=5e5+5;
int n,T,L,R,logn;
long long ans;
int s[N],mx[N][25],Log[N],pos[N][25];
struct zfzgod{
int i,l,r,j,sum;
bool operator <(zfzgod i)const{
return sum<i.sum;
}
};
priority_queue<zfzgod> q;
int main(){
n=read(),T=read(),L=read(),R=read();logn=log2(n)+1;Log[0]=-1;
for(int i=1,x;i<=n;i++){
x=read();
mx[i][0]=s[i]=s[i-1]+x;
pos[i][0]=i;
Log[i]=Log[i>>1]+1;
}
for(int j=1;j<=logn;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
if(mx[i][j-1]>mx[i+(1<<j-1)][j-1]){
mx[i][j]=mx[i][j-1];
pos[i][j]=pos[i][j-1];
}
else{
mx[i][j]=mx[i+(1<<j-1)][j-1];
pos[i][j]=pos[i+(1<<j-1)][j-1];
}
}
}
for(int i=1;i<=n-L+1;i++){
int k=Log[min(i+R-1,n)-(i+L-1)+1];
int x,y;
if(mx[i+L-1][k]>mx[min(i+R-1,n)-(1<<k)+1][k]){
x=mx[i+L-1][k];
y=pos[i+L-1][k];
}
else{
x=mx[min(i+R-1,n)-(1<<k)+1][k];
y=pos[min(i+R-1,n)-(1<<k)+1][k];
}
q.push((zfzgod){i,i+L-1,min(i+R-1,n),y,x-s[i-1]});
}
while(T--){
zfzgod tmp=q.top();q.pop();
int x,y,k=Log[tmp.j-tmp.l];
ans+=tmp.sum;
if(tmp.j>tmp.l){
if(mx[tmp.l][k]>mx[tmp.j-(1<<k)][k]){
x=mx[tmp.l][k];
y=pos[tmp.l][k];
}
else{
x=mx[tmp.j-(1<<k)][k];
y=pos[tmp.j-(1<<k)][k];
}
q.push((zfzgod){tmp.i,tmp.l,tmp.j-1,y,x-s[tmp.i-1]});
}
if(tmp.j<tmp.r){
k=Log[tmp.r-tmp.j];
if(mx[tmp.j+1][k]>mx[tmp.r-(1<<k)+1][k]){
x=mx[tmp.j+1][k];
y=pos[tmp.j+1][k];
}
else{
x=mx[tmp.r-(1<<k)+1][k];
y=pos[tmp.r-(1<<k)+1][k];
}
q.push((zfzgod){tmp.i,tmp.j+1,tmp.r,y,x-s[tmp.i-1]});
}
}
printf("%lld",ans);
return 0;
}
神题没有总结。

浙公网安备 33010602011771号