zoukankan      html  css  js  c++  java
  • SDOI2017

    [SDOI2017]硬币游戏
    [SDOI2017]天才黑客
    题目中的代价与这次走的边和上次走的边有关。
    显然可以这样建边:
    设f[x][i]表示x节点上次走的边为i的最小代价。
    f[x][i]向x有边的节点f[y][j]连边。
    两条边的代价是trie树上的lca
    但是显然无法ac。
    考虑换一种建图方式。
    把一条边拆成两个点:出点和入点,点之间连权值为边权的边
    对于两条边(a,b),(c,d)来讲,如果b=c,那么第一个边的出点向第二个边的入点连一条权值为dep[lca]的边。
    但是显然也无法通过。
    这样子建图点数是原图的边数,无法优化,可以考虑优化新图的边数。
    把一个点的所有入/出点按照lca排序,新建一排点表示每条边。
    如果我们按照sa那样求height数组表示相邻两个串的lca的深度,则每条边的代价就是它对应的区间的height数组的min值。
    考虑一个点的贡献,是一个区间,用单调栈处理然后线段树优化连边,时间复杂度为两个log。
    考虑继续进行优化。新建4排点:a1,a2,b1,b2,a1,a1用于处理前缀,b1,b2用于处理后缀。
    设b[i]表示所有被排序的点的第i个。
    还是把每条边拆成一个点。
    a1,a2上的每个点i向i-1连边,b1,b2的每个点i->i+1连边。
    现在处理前缀连边,注意到每条边的意义是:i节点可以通过dep[lca(b[i],b[i-1])]的代价访问到前面的点。所以a1的第i个点向a2的第i-1个点连边。
    处理b1,b2同理。
    最终把a2,b2向原来的边连边权为0的边即可。
    在跑最短路时要加上原来的边权。
    时间复杂度mlogm,常数较大。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000010
    #define int long long
    int n,m,k,d[N],a,b,c[N],f[N][20],de[N],h[N],v[N],nxt[N],ec,id[N],cc,p1[N],p2[N],s1[N],s2[N],va[N],c1,ct,dd[N],vi[N],cv;
    struct no{
    	int x,d;
    };
    int operator <(no x,no y){
    	return x.d>y.d;
    }
    int cp(int x,int y){
    	return id[d[abs(x)]]<id[d[abs(y)]];
    }
    vector<no>g[N];
    vector<int>in[N],ou[N];
    priority_queue<no>q;
    void add(int x,int y){v[++ec]=y;nxt[ec]=h[x];h[x]=ec;}
    void adj(int x,int y,int z){g[x].push_back((no){y,z});}
    void dfs(int x,int fa){
    	f[x][0]=fa;
    	id[x]=++cc;
    	for(int i=h[x];i;i=nxt[i])
    		if(v[i]!=fa){
    			de[v[i]]=de[x]+1;
    			dfs(v[i],x);
    		}
    }
    int lc(int x,int y){
    	if(de[x]>de[y])swap(x,y);
    	for(int i=19;~i;i--)
    		if(de[f[y][i]]>=de[x])
    			y=f[y][i];
    	if(x==y)return x;
    	for(int i=19;~i;i--)
    		if(f[x][i]!=f[y][i])
    			x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    int dij(){
    	while(!q.empty()){
    		no x=q.top();
    		q.pop();
    		if(vi[x.x])continue;
    		vi[x.x]=1;
    		for(int i=0;i<g[x.x].size();i++)
    			if(dd[g[x.x][i].x]>dd[x.x]+g[x.x][i].d+c[g[x.x][i].x]){
    				dd[g[x.x][i].x]=dd[x.x]+g[x.x][i].d+c[g[x.x][i].x];
    				q.push((no){g[x.x][i].x,dd[g[x.x][i].x]});
    			}
    	}
    }
    signed main(){
    	//freopen("r.in","r",stdin);
    	//freopen("w.out","w",stdout);
    	int t;
    	cin>>t;
    	while(t--){
    		cin>>n>>m>>k;
    		memset(dd,63,sizeof(dd));
    		memset(c,0,sizeof(c));
    		for(int i=1;i<=m;i++){
    			int x,y;
    			cin>>x>>y>>c[i]>>d[i];
    			in[y].push_back(i);
    			ou[x].push_back(i);
    			if(x==1){
    				q.push((no){i,c[i]});
    				dd[i]=c[i];
    			}
    		}
    		for(int i=1;i<k;i++){
    			int x,y,z;
    			cin>>x>>y>>z;
    			add(x,y);
    			add(y,x);
    		}
    		dfs(1,0);
    		f[1][0]=1;
    		for(int i=1;i<20;i++)
    			for(int j=1;j<=k;j++)
    				f[j][i]=f[f[j][i-1]][i-1];
    		ct=m;
    		for(int i=1;i<=n;i++){
    			c1=0;
    			for(int j=0;j<in[i].size();j++)
    				va[++c1]=in[i][j];
    			for(int j=0;j<ou[i].size();j++)
    				va[++c1]=-ou[i][j];
    			sort(va+1,va+c1+1,cp);
    			for(int j=1;j<=c1;j++){
    				p1[j]=++ct;
    				s1[j]=++ct;
    				p2[j]=++ct;
    				s2[j]=++ct;
    			}
    			for(int j=1;j<=c1;j++){
    				if(j>1){
    					adj(p1[j],p1[j-1],0);
    					adj(p2[j],p2[j-1],0);
    				}
    				if(j<c1){
    					adj(s1[j],s1[j+1],0);
    					adj(s2[j],s2[j+1],0);
    				}
    				if(va[j]>0){
    					adj(va[j],p1[j],0);
    					adj(va[j],s1[j],0);
    				}
    				else{
    					adj(s2[j],-va[j],0);
    					adj(p2[j],-va[j],0);
    				}
    			}
    			for(int j=2;j<=c1;j++){
    				int v=de[lc(d[abs(va[j])],d[abs(va[j-1])])];
    				adj(p1[j],p2[j-1],v);
    			}
    			for(int j=1;j<c1;j++){
    				int v=de[lc(d[abs(va[j])],d[abs(va[j+1])])];
    				adj(s1[j],s2[j+1],v);
    			}
    		}
    		dij();
    		for(int i=2;i<=n;i++){
    			int r=1e18;
    			for(int j=0;j<in[i].size();j++)
    				r=min(r,dd[in[i][j]]);
    			cout<<r<<'
    ';
    		}
    		ec=cc=ct=0;
    		memset(h,0,sizeof(h));
    		memset(vi,0,sizeof(vi));
    		for(int i=0;i<N;i++){
    			in[i].clear();
    			ou[i].clear();
    			g[i].clear();
    		}
    	}
    }
    
    

    [SDOI2017]新生舞会
    简单题,直接分数规划后费用流即可
    [SDOI2017]遗忘的集合
    [SDOI2017]相关分析
    [SDOI2017]数字表格
    [SDOI2017]文本校正
    分类讨论。文本串共有3!种拼接方式。
    如果原串abc代表新串abc,直接判断。
    如果原串abc代表新串bca。
    原串a|bc代表新串bc|a
    枚举a的长度,分两端哈希。
    如果原串abc代表新串cab,可以仿照上面一种情况做。
    如果原串abc代表新串cba。
    考虑把T倒序插入S
    即生成新的串(ss=s_1t_ns_2t_n-1...s_nt_1)
    如果一个长度为偶数,开头坐标为奇数的串[l,r]是回文串。
    如果a为ss[l]在s中对应的编号,b在ss[r]在s中对应的编号,中间有k个属于t的字符。
    (s[a...a+k-1]==t[b-k+1...b])
    所以问题就是判定ss是否能够划分为3个回文串。
    manacher一下原串。枚举第一个回文串,接下来就是判断双回文串。
    根据jcvb字符串课件,一个双回文串st一定能表示成ab,其中a为st的最长回文前缀或者b为st的最长回文后缀。
    (f[i])表示以i开头的最长回文串,(g[i])表示以i结尾的最长回文串,可以dp解决,具体看代码。
    如果前面的回文串是([1,i]),且则需要判定(ss[i...n*2])是否为双回文串。
    要判定(a)是否是(ss[i...n*2])的最长回文前缀,则要判定(ss[f[i]+1...n*2])是否是回文串。manacher处理。
    要判定(b)是否是(ss[i...n*2])的最长回文后缀,使用一个队列(q)存储(ss[1...n*2])的所有回文后缀开始的位置(i).
    如果(q)的头部(<i),则删除(q)的头部。
    删完以后(q)的头部(x)就是(ss[i...n*2])的最长回文后缀。
    则判定(ss[i...x-1])是否是回文串即可。
    如果原串(abc)代表新串(acb)
    考虑倒着枚举(a)的长度,则题目要求每次在开头插入一个字符/判断是否合法。
    可以按照上面的方法做。
    但是每次开头的(ss[2])都会变化,不太能做。
    注意到我们寻找最长双回文串的过程,等价于寻找最大的p,使得(s[i...p]==t[p+1,n]),使用哈希计算剩余的部分即可。
    如果原串(abc)代表新串(bac),可以仿照上面一种情况做。
    代码很难写。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define N 2000010
    #define mo 998244353
    #define bb 1000001
    int T,n,m,s[N],t[N],s1[N],t1[N],pr[N],sf[N],ss[N],tp;
    struct hs{
    	int n,bs[N],hs[N];
    	void gt(int *a,int m){
    		n=m;
    		bs[0]=1;
    		for(int i=1;i<=m;i++)
    			bs[i]=bs[i-1]*bb%mo;
    		for(int i=1;i<=m;i++)
    			hs[i]=(hs[i-1]+a[i]*bs[i])%mo;
    	}
    	int gg(int l,int r){
    		return (hs[r]-hs[l-1]+mo)%mo*bs[n-r]%mo;
    	}
    	void cl(){
    		for(int i=1;i<=n;i++)
    			bs[i]=hs[i]=0;
    		n=0;
    	}
    }a,b;
    struct km{
    	int n,f[N],b[N];
    	void gt(int *a,int m){
    		n=m;
    		for(int i=1;i<=m;i++)
    			b[i]=a[i];
    		int j=0;
    		for(int i=2;i<=m;i++){
    			while(j&&b[j+1]!=b[i])
    				j=f[j];
    			if(b[j+1]==b[i])j++;
    			f[i]=j;
    		}
    	}
    	void gg(int &p,int c){
    		while(p&&b[p+1]!=c)
    			p=f[p];
    		if(b[p+1]==c)
    			p++;
    	}
    	void cl(){
    		for(int i=1;i<=n;i++)
    			b[i]=f[i]=0;
    		n=0;
    	}
    }c,d;
    struct pl{
    	int n,f[N],b[N];
    	void gt(int *a,int m){
    		n=m;
    		for(int i=1;i<=n;i++)
    			b[i]=a[i];
    		int p=0,md=0;
    		b[m+1]=1e9;
    		for(int i=1;i<=n;i++){
    			if(p>i)
    				f[i]=min(p-i,f[md*2-i]);
    			else f[i]=0;
    			while(i>f[i]&&i+f[i]+1<=n&&b[i-f[i]]==b[i+f[i]+1])
    				f[i]++;
    			if(i+f[i]>p){
    				p=i+f[i];
    				md=i;
    			}
    		}
    	}
    	int gg(int l,int r){
    		if((r-l+1)&1)return 0;
    		int lv=(r-l+1)/2,le=l+lv-1;
    		if(f[le]>=lv)return 1;
    		return 0;
    	}
    	void cl(){
    		for(int i=1;i<=n+1;i++)
    			f[i]=b[i]=0;
    		n=0;
    	}
    }e;
    signed main(){
    	//freopen("r.in","r",stdin);
    	cin>>T;
    	while(T--){
    		a.cl();
    		b.cl();
    		c.cl();
    		d.cl();
    		e.cl();
    		cin>>n>>m;
    		int ok=1,o1=1;
    		for(int i=1;i<=n;i++)
    			scanf("%lld",&s[i]);
    		for(int i=1;i<=n;i++){
    			scanf("%lld",&t[i]);
    			if(s[i]!=t[i])o1=0;
    		}
    		if(o1){
    			puts("YES");
    			continue;
    		}
    		o1=0;
    		a.gt(s,n);
    		b.gt(t,n);
    		for(int i=1;i<n;i++)//abc bca
    			if(!o1&&a.gg(1,i)==b.gg(n-i+1,n)&&a.gg(i+1,n)==b.gg(1,n-i)){
    				puts("YES");
    				o1=1;
    			}
    		for(int i=2;i<=n;i++)//abc cab
    			if(!o1&&a.gg(1,i)==b.gg(n-i+1,n)&&a.gg(i+1,n)==b.gg(1,n-i)){
    				puts("YES");
    				o1=1;
    			}
    		for(int i=1;i<=n;i++){
    			s1[i]=s[n-i+1];
    			t1[i]=t[n-i+1];
    		}
    		c.gt(s1,n);
    		d.gt(t1,n);
    		int p1=0,p2=0;
    		for(int i=n;i>1;i--){//abc acb
    			c.gg(p1,t[i]);
    			d.gg(p2,s[i]);
    			if(p2&&!o1&&a.gg(1,i-1)==b.gg(1,i-1)&&a.gg(i+p2,n)==b.gg(i,n-p2)){
    				puts("YES");
    				o1=1;
    			}
    			if(p1&&!o1&&a.gg(1,i-1)==b.gg(1,i-1)&&a.gg(i,n-p1)==b.gg(i+p1,n)){
    				o1=1;
                                    puts("YES");
    			}
    		}
    		c.cl();
    		d.cl();
    		c.gt(s,n);
    		d.gt(t,n);
    		p1=p2=0;
    		for(int i=1;i<n;i++){//abc bac
    			c.gg(p1,t[i]);
    			d.gg(p2,s[i]);
    			if(!o1&&a.gg(i+1,n)==b.gg(i+1,n)&&a.gg(p1+1,i)==b.gg(1,i-p1)){
    				puts("YES");
    				o1=1;
    			}
    			if(!o1&&a.gg(i+1,n)==b.gg(i+1,n)&&a.gg(1,i-p2)==b.gg(p2+1,i)){
    				puts("YES");
    				o1=1;
    			}
    		}
    		p1=p2=1;
    		for(int i=1;i<=2*n;i++){
    			if(i%2==1){
    				s1[i]=s[p1];
    				p1++;
    			}
    			else{
    				s1[i]=t[n-p2+1];
    				p2++;
    			}
    		}
    		e.gt(s1,n*2);
    		for(int i=1;i<=n*2;i++)
    			sf[i]=pr[i]=i;
    		for(int i=1;i<=n*2;i++){
    			pr[i-e.f[i]+1]=max(pr[i-e.f[i]+1],i+e.f[i]);
    			sf[i+e.f[i]]=min(sf[i+e.f[i]],i-e.f[i]+1);
    		}
    		for(int i=2;i<=n*2;i++)
    			pr[i]=max(pr[i],pr[i-1]-1);
    		for(int i=n*2-1;i;i--)
    			sf[i]=min(sf[i],sf[i+1]+1);
    		tp=0;
    		for(int i=1;i<=n*2;i++)
    			if(pr[i]==n*2)ss[++tp]=i;
    		int po=1;
    		if(T==2){
    			T++;
    			T--;
    		}
    		for(int i=2;i<=n*2;i+=2)
    			if(sf[i]==1){
    				int l=i+1,r=pr[i+1];
    				if(!o1&&e.gg(r+1,n*2)){
    					puts("YES");
    					o1=1;
    				}
    				while(ss[po]<=i)po--;
    				l=ss[po];
    				r=n;
    				if(!o1&&e.gg(i+1,l-1)&&po){
    					puts("YES");
    					o1=1;
    				}
    			}
    		if(!o1)puts("NO");
    		for(int i=1;i<=2*n;i++)
    			pr[i]=sf[i]=0;
    	}
    }
    
    

    [SDOI2017]苹果树
    [SDOI2017]龙与地下城
    https://www.cnblogs.com/cszmc2004/p/12963732.html
    [SDOI2017]树点涂色
    用lct维护颜色相同的连续段。
    如果把每个节点x的重儿子p[x]设为和它颜色相同的点的编号。
    则一次染色操作就是把x->根节点的点access一下,使用重链把这些点连起来。
    如果设f[x]表示x->根节点的虚边个数。
    由于每次染的颜色都是不同的,所以f[x]就是x节点到根节点的色数。
    使用一个线段树st。
    如果一条重边->虚边,则子树dfs序+1否则-1。
    查询就是区间最大值。
    [SDOI2017]序列计数
    [SDOI2017]切树游戏

  • 相关阅读:
    lumen简单使用exel组件
    VIM 批量注释的两种方法 (转)
    linux时间校准 设置时间为上海时区
    lumen发送邮件配置
    centos 下安装redis 通过shell脚本
    shell 脚本接收参数
    linux设置系统变量
    linux通配符
    UCCI协议[转]
    一种编程范式:对拍编程
  • 原文地址:https://www.cnblogs.com/cszmc2004/p/13228204.html
Copyright © 2011-2022 走看看