noip模拟测试23
考试总结:这次考试,成绩和自己的预估分数差距有点大,问题主要是出在了第一题上,我当时读完题,就想到了线段树,很快就码完了,但是我当时没有想到的两个关键问题是:1.没有想到离散化 2.对于异或的情况没有分类讨论,导致我第一题爆零,其他的题还好,基本上拿到了自己预估的分数。这次考试的教训就是对于自己认为想到的正解多想想一些细节问题,不能想到啥就打啥。
T1 联
思路:很显然,一道线段树裸题,我的算法是记录每个区间的1的数量之和,最后查询的时候只需要按照先左后右的顺序查询当前区间里1的数量和区间长度的大小即可,但是注意对于异或的操作进行分类讨论,具体实现见代码:
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e7+10;
const int M=1e6+10;
int m,ty,ul,ur,cnt;
int lsh[M];
vector<int> v[N];
struct CUN
{
int sum,lazy;
}use[N<<2];
struct Segment_tree
{
iv pp(int rt)
{
use[rt].sum=use[lc].sum+use[rc].sum;
}
iv pd(int rt,int l,int r)
{
if(use[rt].lazy)
{
if(use[rt].lazy==1)
{
use[lc].lazy=use[rc].lazy=use[rt].lazy;
use[lc].sum=mid-l+1;
use[rc].sum=r-mid;
}
else if(use[rt].lazy==2)
{
use[lc].lazy=use[rc].lazy=use[rt].lazy;
use[lc].sum=0;
use[rc].sum=0;
}
else
{
if(use[lc].lazy==1)
{
use[lc].lazy=2;
use[lc].sum=0;
}
else if(use[lc].lazy==2)
{
use[lc].lazy=1;
use[lc].sum=mid-l+1;
}
else if(use[lc].lazy==3)
{
use[lc].lazy=0;
use[lc].sum=mid-l+1-use[lc].sum;
}
else
{
use[lc].lazy=3;
use[lc].sum=mid-l+1-use[lc].sum;
}
if(use[rc].lazy==1)
{
use[rc].lazy=2;
use[rc].sum=0;
}
else if(use[rc].lazy==2)
{
use[rc].lazy=1;
use[rc].sum=r-mid;
}
else if(use[rc].lazy==3)
{
use[rc].lazy=0;
use[rc].sum=r-mid-use[rc].sum;
}
else
{
use[rc].lazy=3;
use[rc].sum=r-mid-use[rc].sum;
}
}
use[rt].lazy=0;
}
}
iv change(int rt,int l,int r,int L,int R,int z)
{
if(L<=l&&r<=R)
{
if(z==1)
{
use[rt].sum=r-l+1;
use[rt].lazy=1;
}
else if(z==2)
{
use[rt].sum=0;
use[rt].lazy=2;
}
else
{
if(use[rt].lazy==3)
{
use[rt].lazy=0;
use[rt].sum=r-l+1-use[rt].sum;
}
else if(use[rt].lazy==1)
{
use[rt].lazy=2;
use[rt].sum=0;
}
else if(use[rt].lazy==2)
{
use[rt].lazy=1;
use[rt].sum=r-l+1;
}
else
{
use[rt].sum=r-l+1-use[rt].sum;
use[rt].lazy=3;
}
}
return;
}
pd(rt,l,r);
if(mid>=L)
{
change(lc,l,mid,L,R,z);
}
if(mid<R)
{
change(rc,mid+1,r,L,R,z);
}
pp(rt);
}
ii query(int rt,int l,int r)
{
if(l==r)
return lsh[l];
pd(rt,l,r);
if(use[lc].sum<(mid-l+1))
return query(lc,l,mid);
return query(rc,mid+1,r);
}
}T;
ii read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return (f)?x:(-x);
}
signed main()
{
m=read();
int maxx=0;
for(re i=1;i<=m;i++)
{
ty=read();
ul=read();
ur=read();
v[i].push_back(ty);
v[i].push_back(ul);
v[i].push_back(ur);
maxx=max(maxx,ur);
lsh[++cnt]=ul;
lsh[++cnt]=ur;
lsh[++cnt]=ul+1;
lsh[++cnt]=ur+1;
}
lsh[++cnt]=1;
maxx++;
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
maxx=lower_bound(lsh+1,lsh+cnt+1,maxx)-lsh;
for(re i=1;i<=m;i++)
{
v[i][1]=lower_bound(lsh+1,lsh+cnt+1,v[i][1])-lsh;
v[i][2]=lower_bound(lsh+1,lsh+cnt+1,v[i][2])-lsh;
}
int ans=0;
for(re i=1;i<=m;i++)
{
ans=0;
T.change(1,1,maxx,v[i][1],v[i][2],v[i][0]);
ans=T.query(1,1,maxx);
printf("%lld\n",ans);
}
return 0;
}
T2 赛
思路:这道题我们利用一个多指针的思想,首先先将物品分成四类,AB都喜欢,A喜欢B不喜欢,A不喜欢B喜欢,AB都不喜欢,这样我们将他们存储起来并从大到小排序,然后先假设全部选择AB都喜欢的物品,接着将指针进行移动,我在程序中标记了4个我的出错点,具体实现见代码:
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=2e5+10;
const int INF=1e15;
struct CUN
{
int v;
friend bool operator < (CUN a,CUN b)
{
return a.v<b.v;
}
}cun[N],use1[N],use2[N],use3[N],use4[N];
bool vis1[N],vis2[N];
int n,m,k,a,b;
int cnt1,cnt2,cnt3,cnt4,c1,c2,c3,c4;
int ans=INF,out;
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
inline bool pd()
{
if(cnt1>=m&&m<k)
return 1;
if(cnt1+cnt2<k||cnt1+cnt3<k||k*2>m+cnt1)//3
return 1;
return 0;
}
#undef int
int main()
{
#define int long long
n=read();
m=read();
k=read();
for(re i=1;i<=n;i++)
cun[i].v=read();
a=read();
for(re i=1;i<=a;i++)
vis1[read()]=1;
b=read();
for(re i=1;i<=b;i++)
vis2[read()]=1;
for(re i=1;i<=n;i++)
{
if(vis1[i]&&vis2[i])
use1[++cnt1].v=cun[i].v;
else if(vis1[i])
use2[++cnt2].v=cun[i].v;
else if(vis2[i])
use3[++cnt3].v=cun[i].v;
else
use4[++cnt4].v=cun[i].v;
}
if(pd())
{
printf("-1\n");
return 0;
}
sort(use1+1,use1+cnt1+1);
sort(use2+1,use2+cnt2+1);
sort(use3+1,use3+cnt3+1);
sort(use4+1,use4+cnt4+1);
c1=min(cnt1,m);
if(c1<m)//1
{
c2=c3=max(0ll,k-cnt1);
int now=m-c1-c2-c3;
while(now--)
{
if(use2[c2+1]<use3[c3+1]&&use2[c2+1]<use4[c4+1])
c2++;
else if(use3[c3+1]<use2[c2+1]&&use3[c3+1]<use4[c4+1])
c3++;
else
c4++;
}
}
for(re i=1;i<=c1;i++)
out+=use1[i].v;
for(re i=1;i<=c2;i++)
out+=use2[i].v;
for(re i=1;i<=c3;i++)
out+=use3[i].v;
for(re i=1;i<=c4;i++)
out+=use4[i].v;
ans=min(ans,out);
while(c1)
{
c1--;
out-=use1[c1+1].v;
int fl=0;
if(c1+c2<k&&c2<cnt2)
{
++c2;
out+=use2[c2].v;
++fl;
}
if(c1+c3<k&&c3<cnt3)
{
++c3;
out+=use3[c3].v;
++fl;
}
if(fl==2)
{
if(c4==0)
break;//2
out-=use4[c4].v;
--c4;
}
else if(!fl)//4
{
if(use2[c2+1].v<use3[c3+1].v&&use2[c2+1].v<use4[c4+1].v)
{
++c2;
out+=use2[c2].v;
}
else if(use3[c3+1].v<use2[c2+1].v&&use3[c3+1].v<use4[c4+1].v)
{
++c3;
out+=use3[c3].v;
}
else
{
++c4;
out+=use4[c4].v;
}
}
ans=min(ans,out);
}
printf("%lld\n",ans);
return 0;
}
T3 题
思路:
但是,我们不能暴力枚举每个状态,但是我们可以利用逆向思维反向推出合法的状态,具体实现见代码:
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=410;
const int M=5e4+10;
struct CUN
{
int u,v;
}use[M];
int n,m,ans;
int f[N];
bitset<N>g[N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
int main()
{
n=read();
m=read();
for(re i=1;i<=m;i++)
{
use[i].u=read();
use[i].v=read();
}
for(re i=1;i<=n;i++)
{
f[i]=1;
g[i][i]=1;
for(re j=m;j;j--)
{
int U=use[j].u,V=use[j].v;
if(g[i][U]&&g[i][V])
{
f[i]=0;
break;
}
if((!g[i][U])&&(!g[i][V]))
continue;
g[i][U]=1;
g[i][V]=1;
}
}
for(re i=1;i<=n;i++)
{
if(!f[i])
continue;
for(re j=i+1;j<=n;j++)
{
if(!f[j])
continue;
if((g[i]&g[j])!=0)
continue;
++ans;
}
}
printf("%d\n",ans);
return 0;
}