zoukankan      html  css  js  c++  java
  • HNOI2018省队集训 Day4&5

    HNOI2018省队集训 Day4&5

    Day4很毒,先鸽了(HN队长都鸽了,所以不能怪我)

    marshland

    前方有一片沼泽地.

    方便地, 我们用一个 n × n 的网格图来描述它, 每一个格子代表着沼泽地 的一小片区域. 其中 (1, 1) 代表网格图的左上角, (n, n) 代表网格图的右下角. 若用 X 表示行数, Y 表示列数, 那么 X + Y 为奇数的格子有一个危险度 VX,Y , X + Y 为偶数的格子的危险度为 0.

    为了保障人们的安全, 你有 m 个长相怪异的大石头, 你可以选一些石头 放在网格图上的某些格子上, 石头可以看成一个 ‘L’ 形的块, 并且占三个格子, 它通过旋转有四种方式供放置, 仅会使得在拐角处的那个格子危险度减为 0.

    网格图中还有 k 个位置是 “禁止位置”, 石头的任何部位都不能位于这些 格子上, 且这些位置的危险度一定为 0.

    现在你需要知道放置一些石头后最小的危险度之和是多少. (石头可以不放完)

    考虑费用流,最开始我考虑的是把选择每个石头抽象成点,有个常见的建法:

    graph 3.png

    这样4、3里面只能选一个,3、6里面只能选一个,并且436每个最多选一次,貌似很符合这道题。但是这道题的每个限制是选了一个其他的无法再选,显然这样的模型无法保证(1-5-6,1-2-3)。

    那么考虑对于每个点只能选一次这个限制和每个点属于哪个L形,容易发现L形可以被拆分为三个点:奇数列的偶数行点,奇数列的奇数行点,偶数列的偶数行点。这么做我们就只需要用上面的模型的后半部分(每个点被选一次),建立三排点。而且由于这些点行数和列数的奇偶性不一样,这样做就是对的。

    关于计算答案,只需要把中间的点拆开加上一条(-w,1)的边即可。

    注意跑费用流的时候不能dfs多路增广,需要找一条增广路增广,保证最大流每次+1,流到m或者答案不再减少直接退出即可。

    party

    显然party会在LCA出召开,每个点选的范围就是当前点到LCA的路径。

    那么我们可以用(O(nfrac m omega))树剖+线段树建立,(O(qclog nfrac m omega))查询每个询问中每个点可选集合。

    对于每个询问怎么求答案呢?考虑到每个权值只能被选一次,题解告诉我们可以用二分图+Hall定理做。

    Hall定理:二分图G存在完美匹配当且仅当X中的任意k个点至少与Y中的k个点相邻

    我们可以(2^cfrac m omega)求出当前点的集合可选权值种类的并集(f(S)) (注意这里可以递推来减少常数),然后对于每个集合(|S|)考虑k。因为答案一定是(c*x)的形式,就可以构造一个左边(|S|*x)个点,右边(f(S))个点的二分图。 首先对于(k=|S|*x)(x)最大为 (frac{f(S)}{|S|}),因为由于(f(S))的定义左边的点和右边一定是完全联通的,只要总数(|S|*xleq f(S))即可。然后对于其他的至少含有 这个集合的每个点的 左边的x个点中的一个点 的k值的条件一定可以通过构造得到(去掉一些点)。那么总的x的最大值就是满足每个条件的最大值的最小值。

    总复杂度(O(nfrac m w+qclog nfrac m w+q2^cfrac m w))

    最开始写T了,因为没有预处理到链顶的答案。这在没有修改的题好像是常规操作,然而我一直用的是log^2...

    platform

    切了...但是要注意本质不同的子串算不算,我以为是算的,结果WA了一发。要仔细读题和样例解释!

    考虑左端点相同的子串,右端点增大,权值和不减,降序排名单增(前面的串是后面的前缀)。那么2-1仍然单增,二分找到差值(leq 0)的最后一个,判断是否等于0(相等)即可。

    快速找子串排名我用的是反串的SAM的parent树上倍增+预处理出这个节点取到len最大的排名。具体加边方法画个SAM就知道了。总复杂度(nlog^2n),勉强卡过。

    然后题解前半部分跟我是一样的,后半部分SA能做到(log n),是用线段树维护区间赋值和加等差数列,SAM就暴力许多。

    贴个代码吧:

    #include<bits/stdc++.h>
    #define FOR(i,a,b) for(int i=a;i<=b;++i)
    #define ROF(i,a,b) for(int i=a;i>=b;--i)
    #define ll long long
    using namespace std;
    const int N = 4e5+200;
    int read(){
    	int x=0,pos=1;char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
    	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    	return pos?x:-x; 
    } 
    char s[N];int n,w[N],pos[N],f[N][20];
    ll val[N],vs[N],rk[N];
    int las=1,tot=1;
    struct typ{
    	int ch[26],fa,len,pos;
    	typ(){
    		memset(ch,0,sizeof(ch));fa=len=0;
    	}
    }t[N];
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mp make_pair
    vector<pii> edge[N];
    void insert(int c,int id){
    	int p=las,np=las=++tot;pos[id]=np;w[np]=1;t[np].pos=id;
    	t[np].len=t[p].len+1;
    	for(;p&&!t[p].ch[c];p=t[p].fa) t[p].ch[c]=np;
    	if(!p){
    		t[np].fa=1;
    	}else{
    		int q=t[p].ch[c];
    		if(t[p].len+1==t[q].len) t[np].fa=q;
    		else{
    			int nq=++tot;
    			t[nq]=t[q];
    			t[nq].len=t[p].len+1,t[q].fa=t[np].fa=nq;
    			for(;t[p].ch[c]==q;p=t[p].fa) t[p].ch[c]=nq;
    		}
    	}
    }
    int ti[N],lnk[N];
    ll rn=0;
    void dfs1(int now){
    	for(int i=0;i<edge[now].size();i++){
    		int v=edge[now][i].se;
    		dfs1(v),w[now]+=w[v];if(!t[i].pos) t[i].pos=t[v].pos;
    	}
    }
    int cmp(pii a,pii b){
    	return a.fi>b.fi;
    }
    void dfs(int now){
    	sort(edge[now].begin(),edge[now].end(),cmp);
    	for(int i=0;i<edge[now].size();i++){
    		int v=edge[now][i].se;
    		dfs(v);
    	}
    	rk[now]=rn;
    	rn+=1ll*(t[now].len-t[t[now].fa].len);//*w[now];
    }
    ll calcrk(int l,int r){
    	int len=r-l+1,x=pos[l];
    	ROF(i,19,0){
    		if(t[f[x][i]].len>=len) x=f[x][i];
    	}
    	return 1ll+rk[x]+1ll*(t[x].len-len);//*w[x];
    }
    ll calcv(int l,int r){
    	return vs[r]-vs[l-1];
    }
    ll calc(int l,int r){
    	return calcv(l,r)-calcrk(l,r);
    }
    vector<pii> ans;
    int main(){
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	FOR(i,1,n){
    		val[i]=read();vs[i]=vs[i-1]+val[i];
    	}
    	ROF(i,n,1){
    		insert(s[i]-'a',i);
    	}
    	FOR(i,2,tot){
    		f[i][0]=t[i].fa;
    		edge[t[i].fa].push_back({0,i});
    	}
    	dfs1(1);
    	FOR(i,1,tot) edge[i].clear();
    	FOR(i,2,tot){
    		edge[t[i].fa].push_back({s[t[i].pos+t[t[i].fa].len]-'a',i});
    	}
    	dfs(1);
    	FOR(i,1,19){
    		FOR(j,1,tot){
    			f[j][i]=f[f[j][i-1]][i-1];
    		}
    	}
    	FOR(i,1,n){
    		int l=i,r=n+1;
    		while(l<r-1){
    			int mid=(l+r)>>1;
    			if(calc(i,mid)<=0) l=mid;
    			else r=mid;
    		}
    		if(calc(i,l)==0){
    			ans.push_back({i,l}); 
    		}
    	}
    	printf("%d
    ",ans.size());
    	if(!ans.size()) return 0;
    	FOR(i,0,ans.size()-1){
    		printf("%d %d
    ",ans[i].fi,ans[i].se); 
    	}
    	return 0;
    }
    
  • 相关阅读:
    windows中dos命令指南
    HDU 2084 数塔 (dp)
    HDU 1176 免费馅饼 (dp)
    HDU 1004 Let the Balloon Rise (map)
    变态杀人狂 (数学)
    HDU 2717 Catch That Cow (深搜)
    HDU 1234 开门人和关门人 (模拟)
    HDU 1070 Milk (模拟)
    HDU 1175 连连看 (深搜+剪枝)
    HDU 1159 Common Subsequence (dp)
  • 原文地址:https://www.cnblogs.com/lcyfrog/p/13024371.html
Copyright © 2011-2022 走看看