Loli不是你们想的loli
20180302 Loli模拟赛
先解释一下,loli不是你们想象的那个……那是我们信息组总教练的外号,对,就是那个上次一口气出了五道奶牛题的神级教练(非黑)。
这次这个题也是有毒……两道板子+一道毒瘤题……
T1 ditch
这不就是最大流板子?说的还挺玄乎的……还是个奶牛题……
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define rint register int
#define inv inline void
#define ini inline int
#define inb inline bool
#define big 10000010
#define maxn 1010
using namespace std;
int m,n,cnt,tot,ans;
int head[maxn],cur[maxn],dis[maxn];
ini read()
{
char c;rint r=0,f=1;
c=getchar();
while (c<'0' || c>'9') {
if (c=='-') f=-1;
c=getchar();
}
while (c>='0' && c<='9'){
r=r*10+(c^48);
c=getchar();
}
return r*f;
}
struct node
{
int next,to,dis,cnt;
}ljb[1001];
inv add_edge(int x,int y,int z)
{
ljb[++cnt].next=head[x];
ljb[cnt].to=y;
ljb[cnt].dis=z;
ljb[cnt].cnt=cnt;
head[x]=cnt;
}
inv add(int x,int y,int z)
{
add_edge(x,y,z);
add_edge(y,x,0);
}
queue <int> q;
inb bfs()
{
for (rint i=1;i<=n;i++) dis[i]=-1;
q.push(1);dis[1]=0;
while (!q.empty()){
int x=q.front();q.pop();
for (rint i=head[x];i!=-1;i=ljb[i].next){
int y=ljb[i].to;
if (dis[y]<0 && ljb[i].dis>0){
dis[y]=dis[x]+1;
q.push(y);
}
}
}
if (dis[n]>0) return 1;
return 0;
}
ini dfs(int x,int low)
{
if (x==n) return low;
for (rint& i=cur[x];i!=-1;i=ljb[i].next)
{
int y=ljb[i].to;
if (dis[y]==dis[x]+1 && ljb[i].dis>0)
{
int flow=dfs(y,min(low,ljb[i].dis));
if (flow)
{
ljb[i].dis-=flow;
ljb[ljb[i].cnt^1].dis+=flow;
return flow;
}
}
}
return 0;
}
int main()
{
freopen("ditch.in","r",stdin);
freopen("ditch.out","w",stdout);
m=read();n=read();
for (rint i=1;i<=n;i++) head[i]=-1;cnt=-1;
for (rint i=1;i<=m;i++){
int x,y,z;
x=read();y=read();z=read();
add(x,y,z);
}
while (bfs())
{
for (rint i=1;i<=n;i++) cur[i]=head[i];
do
{
tot=dfs(1,big);
ans+=tot;
}
while (tot);
}
printf("%d",ans);
}
T2 manager
好像是树剖板子?然而蒟蒻的我好像忘了树剖怎么打?
哎,看这个题好像只需要处理子树和到根节点两种情况就好了,这不就简单了吗……然而我的山寨树剖好像跑的贼慢?不管了能过就成……
当然要注意一个地方,就是卸载一个软件要先卸载子树,安装一个软件要把根节点到它的路径上的软件都安装上,别搞反了……标记的话搞一个覆盖标记,标记为1就是都要选,标记为0就是一个都不选,线段树维护区间和就行了。
还有就是往根节点上跳的时候不能是f[x]为0的时候退出修改或询问,因为当前重链的top不一定是0,得再往上跳到0才行……我们可以把根节点的父亲设成inf,x==inf的时候再退出
#include<iostream>
#include<cstdio>
#include<cstring>
#define rint register int
#define inv inline void
#define ini inline int
#define inb inline bool
#define mid (l+r>>1)
#define ls (num<<1)
#define rs (num<<1|1)
#define big 1e9
#define maxn 100010
using namespace std;
int sum[maxn<<2],add[maxn<<2],res[maxn<<2];
int a[maxn],f[maxn],tot[maxn],dfn[maxn],idfn[maxn],son[maxn],top[maxn],head[maxn],dis[maxn];
int n,q,cnt;
char s[maxn];
struct node
{
int next,to;
}ljb[maxn];
ini read()
{
char c;rint r=0,f=1;
c=getchar();
while (c<'0' || c>'9') {
if (c=='-') f=-1;
c=getchar();
}
while (c>='0' && c<='9'){
r=r*10+(c^48);
c=getchar();
}
return r*f;
}
inv add_edge(int x,int y)
{
ljb[++cnt].next=head[x];
ljb[cnt].to=y;
head[x]=cnt;
}
inv dfs1(int x,int fa)
{
f[x]=fa;tot[x]=1;
if (x) dis[x]=dis[fa]+1;else dis[x]=1;
rint maxx=0;
for (rint i=head[x];i;i=ljb[i].next)
{
int y=ljb[i].to;
if (y!=f[x])
{
dfs1(y,x);
if (tot[y]>maxx) maxx=tot[y],son[x]=y;
tot[x]+=tot[y];
}
}
}
inv dfs2(int x,int tp)
{
dfn[x]=++cnt;
top[x]=tp;
if (son[x]) dfs2(son[x],tp);
for (rint i=head[x];i;i=ljb[i].next)
{
int y=ljb[i].to;
if (y!=f[x] && y!=son[x])
dfs2(y,y);
}
}
inv pushdown(int ln,int rn,int num)
{
if (res[num])
{
add[ls]=0;add[rs]=0;
res[ls]=1;res[rs]=1;
sum[ls]=0;sum[rs]=0;
res[num]=0;
}
if (add[num])
{
add[ls]=1;add[rs]=1;
sum[ls]=ln;sum[rs]=rn;
add[num]=0;
}
}
inv change(int L,int R,int l,int r,int num,int f1,int f2)
{
if (L<=l && r<=R)
{
if (f1)
{
add[num]=1;
sum[num]=r-l+1;
}
if (f2)
{
res[num]=1;
add[num]=0;
sum[num]=0;
}
return;
}
pushdown(mid-l+1,r-mid,num);
if (L<=mid) change(L,R,l,mid,ls,f1,f2);
if (R>mid) change(L,R,mid+1,r,rs,f1,f2);
sum[num]=sum[ls]+sum[rs];
}
ini query(int L,int R,int l,int r,int num)
{
if (L<=l && r<=R) return sum[num];
pushdown(mid-l+1,r-mid,num);
rint ans=0;
if (L<=mid) ans+=query(L,R,l,mid,ls);
if (R>mid) ans+=query(L,R,mid+1,r,rs);
return ans;
}
ini treeq(int x)
{
rint ans=0;
do
{
ans+=query(dfn[top[x]],dfn[x],1,n,1);
x=f[top[x]];
}
while (x!=big);
return ans;
}
inv treec(int x)
{
do
{
change(dfn[top[x]],dfn[x],1,n,1,1,0);
x=f[top[x]];
}
while (x!=big);
}
int main()
{
freopen("manager.in","r",stdin);
freopen("manager.out","w",stdout);
n=read();
for (rint i=1;i<=n-1;i++){
a[i]=read();
add_edge(a[i],i);
}
dfs1(0,big);
cnt=0;
dfs2(0,0);
q=read();
for (rint i=1;i<=q;i++)
{
cin>>s;
rint x=read(),ans=0;
if (s[0]=='i')
{
ans=treeq(x);
treec(x);
printf("%d\n",dis[x]-ans);
}
if (s[0]=='u')
{
ans=query(dfn[x],dfn[x]+tot[x]-1,1,n,1);
change(dfn[x],dfn[x]+tot[x]-1,1,n,1,0,1);
printf("%d\n",ans);
}
}
}
T3 interval
这个玩意有毒吧……
考场上没搓出来,最后输出empty set得了十分,出了一堆脑残错误,不想复述。
更好的解法可以百度搜索“校门外的区间”这道题,正常人的做法比我的做法好理解多了……
这里说说蒟蒻的写法
一开始想分块,然后自我hack之后打的线段树。把一个点拆成三个,闭区间的左右括号都对应第二个点,但是要注意开区间的右括号对应第一个点,开区间的左括号对应第三个点,别搞反了。
还有就是读入的是一个字符串,而数字是好多位的,所以要写个函数读一下。
五种操作分别可以化为多次进行区间覆盖为1,区间覆盖为0,区间异或三种操作来进行,这里大家可以自己推一下,推不出来看底下的程序也能看出来。
对于线段树的每一个区间,有1,0,-1三种可能的值,1是全部为1,0是全部为0,-1是有0也有1,这样能优化后面查询的复杂度。
线段树的两种标记,覆盖和异或,我们可以看出覆盖优于异或。当一段区间有覆盖标记的时候,我们可以把覆盖标记取反,就不用打异或标记了,或者当一段区间为纯色(也就是值为0或1)的时候,我们可以把这个区间反色,然后给子区间打上覆盖标记,这样也不用打异或标记。如果这两种情况都不满足的话,我们再给区间打上一个异或标记,然后pushdown的时候下传,如果子区间满足以上两种情况,就按上面的方法结算,然后再pushup上去,我比较傻逼,只能想到这种笨办法。
查询的话就深搜这棵树,如果搜到纯色0的区间,直接return回来;如果搜到纯色1的区间,给左右区间打标记,然后如果是单点的话就单独打一种标记,输出的时候O(n)枚举每一个点,如果这个点被打过标记就分三种标记讨论一下要不要输出,代码里面关于输出的细节挺多的,手动推演一下比较好理解。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define rint register int
#define inv inline void
#define inc inline char
#define ini inline int
#define mid (l+r>>1)
#define ls (num<<1)
#define rs (num<<1|1)
#define n 196608
using namespace std;
map<char,int> mp;
int sum[n<<2],cov[n<<2],flag[n<<2],a[n<<2];
char c,s[101],ch[101];
inv build() {for (rint i=1;i<=(n<<2);i++) sum[i]=0,cov[i]=-1,flag[i]=0,a[i]=-1;}
inv push_f(int num){
if (cov[num]>=0) {
cov[num]^=1,sum[num]=cov[num],flag[num]=0;
return;
}
if (sum[num]>=0) {
sum[num]^=1,cov[num]=sum[num],flag[num]=0;
return;
}
flag[num]^=1;
}
inv pushdown(int num)
{
if (cov[num]>=0){
cov[ls]=cov[rs]=cov[num];
sum[ls]=sum[rs]=cov[num];
flag[ls]=flag[rs]=0;
cov[num]=-1;
}
if (flag[num]){
push_f(ls);
push_f(rs);
flag[num]=0;
}
}
ini pushup(int num)
{
if (sum[ls]==-1 || sum[rs]==-1) return -1;
if (sum[ls]==sum[rs]) return sum[ls];
return -1;
}
inv change(int L,int R,int l,int r,int num,int tag)
{
if (L<=l && r<=R){
if (tag==0){
cov[num]=0;sum[num]=0;flag[num]=0;
}
if (tag==1){
cov[num]=1;sum[num]=1;flag[num]=0;
}
if (tag==2){
push_f(num); }
return;
}
pushdown(num);
if (L<=mid) change(L,R,l,mid,ls,tag);
if (R>mid) change(L,R,mid+1,r,rs,tag);
sum[num]=pushup(num);
}
inv query(int l,int r,int num)
{
if (sum[num]==1){
a[l]=0;a[r]=1;
if (l==r) a[l]=2;
return;
}
if (sum[num]==0) return;
pushdown(num);
query(l,mid,ls);
query(mid+1,r,rs);
}
inv write(int i,int now)
{
if (!now){
if (i%3==2) putchar('[');
else putchar('(');
printf("%d",(i-1)/3);
putchar(',');
}
else{
printf("%d",(i-1)/3);
if (i%3==2) putchar(']');
else putchar(')');
putchar(' ');
}
}
inv print()
{
rint now=0,fir=0,lst=0,flg=0;
for (rint i=1;i<=n;i++){
if (a[i]>=0){
flg=1;
if (a[i]==0 && a[i-1]>=0) continue;
if (a[i]==1 && a[i+1]>=0) continue;
if (a[i]==2){
if (a[i-1]<0 && a[i+1]<0){
putchar('[');printf("%d",(i-1)/3);
putchar(']');putchar(' ');continue;
}
if (a[i-1]>=0 && a[i+1]>=0) continue;
if (a[i+1]>=0 && now==1) continue;
}
write(i,now);now^=1;
}
a[i-1]=-1;
}
a[n]=-1;
if (!flg) puts("empty set");
}
ini read(int l,int r)
{
rint kk=0;
for (rint i=l;i<=r;i++) kk=kk*10+s[i]-'0';
return kk;
}
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
build();
rint l=0,r=0;
mp[')']=1;mp['(']=3;mp['[']=mp[']']=2;
while (cin>>c){
cin>>s;
for (rint i=1;i<=strlen(s)-1;i++)
if (s[i]==',') {l=read(1,i-1);r=i+1;break;}
r=read(r,strlen(s)-2);
l=l*3+mp[s[0]];
r=r*3+mp[s[strlen(s)-1]];
if (c=='U') change(l,r,1,n,1,1);
if (c=='I'){
change(1,l-1,1,n,1,0);
change(r+1,n,1,n,1,0);
}
if (c=='D') change(l,r,1,n,1,0);
if (c=='C'){
change(1,l-1,1,n,1,0);
change(r+1,n,1,n,1,0);
change(l,r,1,n,1,2);
}
if (c=='S') change(l,r,1,n,1,2);
}
query(1,n,1);
print();
}