【原创题】英勇赛尔,智慧童年!
第一道原创题,,然后第一题就标程出锅了,验题也没验出来orz,太可怕了。
如有重题,纯属巧合。
luogu提交处(最后20%分没有上传)
在这个星球上,迷糊的赛小息将他的n个精灵的属性搞混了。卡璐璐和阿铁打决定帮助赛小息回忆一些信息。卡璐璐可以帮助回忆克制信息,阿铁打可以帮助回忆相生信息,赛小息在感觉信息不对劲的时候会遗忘(撤销)末尾一些信息(可以撤回掉之前的撤回)。
给定T个时刻,每个时刻给出opt,opt为1,卡璐璐给出”克”信息,为2,阿铁打给出”生”信息,为3,赛小息撤回一些信息。他们需要机智的你给出这些精灵的一种方案。为了出题人方便不写SPJ,你只需要每个时刻的最后判断是否合法就可以了。
英勇赛尔,智慧童年!
出题人:newuser 验题人:phoenix (saier.cpp/in/out) 空间128M 时间1500ms题目背景
作为一道优秀的NK互测题,我们需要一道精灵之间打架的差分约束题。 点亮生命的火种 穿梭在浩瀚的太空 追逐那神秘的力量 飒爽英姿蓝色旋风 英勇无畏的赛尔 智慧让我无可阻挡 精灵赐予我力量 心灵就是那战场 起飞吧 英勇智慧的赛尔号问题描述
以上背景与题目关系很大。我们假设我们的主角赛小息来到了一个叫做NK星的地方,这里的精灵有五种属性,分别是火土水风雷。他们的关系已如下。白色的箭头指克,黑色的箭头指生。(例如水克火,土克雷以及风生雷,水生风)
输入格式:
第1行 一个整数n和一个整数T。 第2行至1+T行,每行一个整数opt. Opt==1输入两个整数x和 y 变换之后 表示x克y Opt==2输入两个整数x和 y 变换之后 表示x生y Opt==3输入一个整数x,变换之后 表示遗忘末尾的x次操作信息,(可能会遗忘之前的遗忘(你可以将遗忘也看做一次信息,因此也可以遗忘掉之前的遗忘) ,保证不会非法)输出格式
T行 每行一个字符串,”true”表示合法,”false”表示非法(没有引号!)样例输入1:
5 5 1 3 4 2 4 2 2 2 3 3 1 2 3 2
样例输出1:
true true true true false
样例输入2:
20 20 1 1 2 1 2 3 1 3 4 1 4 5 1 5 6 1 6 7 1 7 8 1 8 9 1 9 10 1 10 11 1 11 12 1 12 13 1 13 14 1 14 15 1 15 16 1 16 17 1 17 18 1 18 19 1 19 20 1 20 1
样例输出2:
true true true true true true true true true true true true true true true true true true true true
数据范围
对于其中10%数据有 T<=8,n<=8 对于其中20%数据有T<=50,n<=30(其中5%数据无3操作) 对于其中20%数据有T<=5000,n<=3000 对于其中30%数据有T<=200000,n<=100000 (其中10%数据无3操作) 对于其中20%数据有T<=2000000,n<=1000000(其中5%数据无3操作) 对于100%的数据T<=1000000,n<=800000,保证数据完全随机由于出题人出不来非随机数据特殊说明:
由于本题读入数据量很大,为保证控制读入效率,这里提供一段读入的代码(本代码会增加程序消耗内存请自行控制内存使用) 你只要把下面的代码贴到你的代码中,然后使用read(x)读入一个整数。 注意它使用了fread,请不要与scanf和cin混用,本地测试的时候读入要使用文件。 本题不强制使用读入优化的代码,你可以自由选择用什么方式读入。无论用什么代码,请都自行测试,并且自己承担相应的风险。需要#include<ctype.h> < pre class="lang:default decode:true">namespace handsome { char buf[1<<20],p1,p2; #define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:p1++) inline int R() { char t=GC; int x=0; while(!isdigit(t)) t=GC; while(isdigit(t)) x=x10+t-48,t=GC; return x; } } using namespace handsome; 比如下面是一个A+B的代码的例子:#includeusing namespace std; namespace handsome{ // .... }; using namespace handsome; int a,b; int main() { a=R(); b=R(); printf("%d\n",a+b); }
题解:
本题有一个很sao的撤回操作,对于这些操作,其实我们可以这样来看-->在没有撤回操作来看,其实就是从0->1->2->3->.....->m,将每个操作看作一个节点,那么就是一条链的遍历。那么对于i号操作是撤回操作,并撤回x条消息,我们就可以看作是他将连在了i-x-1下,以i-x-1为父亲地继续向下进行遍历。 那么在离线的情况下,我们这样构出了一颗dfs树,这样就可以忽视掉撤回的影响了,只是这样我们维护答案的时候必须需要一种可持久化(或者可撤回的)数据结构来维护,使得我们可以在dfs回溯的时候去除这一次操作的影响。 考虑完撤回的影响之后,我们再回头看本题。本题维护如图所示的一个精灵之间的生克关系。基本上就是一眼的带权并查集,只是在路径压缩之后的 并查集并不是如我们之前所想的那样是一个可持久化的数据结构,如果不加任何优化,时间复杂度又肯定错的。那么我们其实只需要在并查集合并的时候,按秩合并一下就可以了。判断一下两个并查集哪个的size更大,将size小的那个并查集插到size大的那个 并查集下面就可以了。 同时,我们将每次并查集添加进去的时候,将在下面的那个添加到另一个栈里面,这样我们在dfs回溯的时候,就可以取出栈中的元素,然后撤销掉并查集的操作就可以了。这样我们就完成了并查集的可持久化。 并查集由于是按秩合并了,没有路径压缩,每次的期望树高是O(log2(n)),每次并查集操作的时间复杂度就变成了O(log2(n))。最终时间复杂度(nlog2(n))(n与T同阶,忽略字母). Newuser's LJ code:/*A KE B val[a]-val[b]=2 A sheng B val[b]-bal[a]=1 */ #includeSkywalker's excellent code:#include #include #include #define mp make_pair using namespace std; const int maxn = 1000005; int n,T,fa[maxn],siz[maxn]; struct node { int opt,x,y; }z[maxn]; int en[maxn],nt[maxn],la[maxn],owo; void addedge(int x,int y) { en[++owo]=y; nt[owo]=la[x]; la[x]=owo; } int val[maxn],bcj[maxn]; namespace handsome { char buf[1<<20],*p1,*p2; #define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++) inline int R() { char t=GC; int x=0; while(!isdigit(t)) t=GC; while(isdigit(t)) x=x*10+t-48,t=GC; return x; } }; using namespace handsome; bool ANS[maxn]; int now = 0; int sta[maxn],top; pair gf(int x) { int sum = 0; while(bcj[x]!=x) { sum=sum+val[x]; x=bcj[x]; } return mp(x,sum); } inline int mo(int x) { x%=5; if(x<0) x+=5; return x; } void chexiao() { int x = sta[top--]; if(x==-1) return; siz[bcj[x]] -= siz[x]; val[x] = 0; bcj[x] = x; } int aha(int x,int y,int cj) { pair tmpx = gf(x); pair tmpy = gf(y); int fx = tmpx.first; int fy = tmpy.first; if(tmpx.first==tmpy.first) { if(mo(mo(tmpx.second)-mo(tmpy.second))!=cj) return 0; else sta[++top]=-1; } else { if(siz[fx]>siz[fy]) { bcj[fy] = fx; val[fy] = -tmpy.second + tmpx.second - cj; siz[fx] += siz[fy]; sta[++top] = fy; } else { bcj[fx] = fy; val[fx] = tmpy.second - tmpx.second + cj; siz[fy] += siz[fx]; sta[++top] = fx; } } return 1; } void dfs(int x,int ba) { if(x==0) { for(int it=la[x];it;it=nt[it]) dfs(en[it],x); return; } fa[x] = ba; if(!ANS[ba]) ANS[x] = 0; else { if(z[x].opt==3) { ANS[x] = ANS[fa[x]]; if(ANS[x]==1) sta[++top]=-1; } else if(z[x].opt==2) { if(aha(z[x].x,z[x].y,4)) ANS[x] = 1; else ANS[x] = 0; } else { if(aha(z[x].x,z[x].y,2)) ANS[x] = 1; else ANS[x] = 0; } } for(int it=la[x];it;it=nt[it]) dfs(en[it],x); if(ANS[ba]&&ANS[x]) { chexiao(); } } int main() { // freopen("saier.in","r",stdin); // freopen("saier.out","w",stdout); n=R(); T=R(); for(int i=1;i<=n;i++) bcj[i]=i,siz[i]=1; for(int i=1;i<=T;i++) { z[i].opt = R(); z[i].x = R(); if(z[i].opt==3) { now-=z[i].x; } else z[i].y = R(); addedge(now,i); now = i; } ANS[0] = 1; dfs(0,0); for(int i=1;i<=T;i++) { if(ANS[i]) printf("true\n"); else printf("false\n"); } }
#include最后:作为Skyfuker的小迷弟Newesur :%%%sto sto sto !Skywalker! orz orz orz%%%#include #include using namespace std; const int N = 1000005; char buf[1<<20],*p1,*p2; #define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++) inline void R(int &x) { char t=GC; x=0; while(!isdigit(t)) t=GC; while(isdigit(t)) x=x*10+t-48,t=GC; } int n,m; struct node { int op,x,y; }ask[N]; int Last[N],Next[N],End[N],cnt; bool ans[N]; int fa[N],dis[N],dep[N]; void Add(int x,int y) { cnt++; End[cnt]=y; Next[cnt]=Last[x]; Last[x]=cnt; } int GetFa(int x,int &d) { if(fa[x]==x){d=0; return x;} int fx=GetFa(fa[x],d); d+=dis[x]; return fx; } void DFS(int u,bool flag) { if(flag) { ans[u]=1; for(int i=Last[u];i;i=Next[i]) { int v=End[i]; DFS(v,flag); } return ; } int op=ask[u].op,x=ask[u].x,y=ask[u].y; if(op==1){ int fx,fy,d1,d2,d,add=0; fx=GetFa(x,d1); fy=GetFa(y,d2); int tmp=(d1+(5-d2%5))%5; if(fx==fy) tmp==3?flag=0:flag=1; else { if(dep[fx]>dep[fy]) swap(fx,fy),d=d1-d2+2; else d=d2-d1+3; d=(d%5+5)%5; fa[fx]=fy,dis[fx]=d; if(dep[fx]==dep[fy]) dep[fy]++,add=1; } if(flag) ans[u]=1; for(int i=Last[u];i;i=Next[i]) { int v=End[i]; DFS(v,flag); } if(fx!=fy) fa[fx]=fx;dis[fx]=0,dep[fy]-add; } else if(op==2){ int fx,fy,d1,d2,d,add=0; fx=GetFa(x,d1); fy=GetFa(y,d2); int tmp=(d1+(5-d2%5))%5; if(fx==fy) tmp==1?flag=0:flag=1; else { if(dep[fx]>dep[fy]) swap(fx,fy),d=d1-d2+4; else d=d2-d1+1; d=(d%5+5)%5; fa[fx]=fy,dis[fx]=d; if(dep[fx]==dep[fy]) dep[fy]++,add=1; } if(flag) ans[u]=1; for(int i=Last[u];i;i=Next[i]) { int v=End[i]; DFS(v,flag); } if(fx!=fy) fa[fx]=fx,dis[fx]=0,dep[fy]-=add; } else{ for(int i=Last[u];i;i=Next[i]) { int v=End[i]; DFS(v,flag); } } } int main() { // freopen("saier.in","r",stdin); // freopen("saier.out","w",stdout); int i; R(n),R(m); for(i=1;i<=n;i++) fa[i]=i,dep[i]=1,dis[i]=0; for(i=1;i<=m;i++) { R(ask[i].op),R(ask[i].x); if(ask[i].op!=3) { R(ask[i].y); Add(i-1,i); } else Add(i-ask[i].x-1,i); } DFS(0,0); for(i=1;i<=m;i++) ans[i]?puts("false"):puts("true"); return 0; }