【训练小结】Petrozavodsk Summer-2014. Moscow SU SG Contest

trac

D-最小回文划分

10个串,问最小回文划分,只要任何一个串是回文即可

和一个串几乎相同,每个串分别维护以i结尾的回文串集合

#include
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const int inf = 2e8;
const int maxn = 100020;
const ll mod = 1e9 + 7;

struct Pali{
	int total,anc[maxn],fail[maxn],len[maxn],last,son[maxn][26],hd[maxn],diff[maxn];
	int S[maxn],m;

	void init() //回文树初始化
	{
    	S[0]=-1;
	    m=0;       //字符串长度
	    total=1;   //回文树点数
    	last=0;    //最后插入的点
    	len[0]=0;  //回文串长度
    	len[1]=-1;
    	fail[0]=fail[1]=1;
	}
	void insert(int ch)
	{
    	S[++m]=ch;
    	int cur=last;
	    while (S[m-len[cur]-1]!=S[m]) cur=fail[cur];
    	if (!son[cur][ch])
   		{
        	len[++total]=len[cur]+2;
        	int tmp=fail[cur];
        	while (S[m-len[tmp]-1]!=S[m]) tmp=fail[tmp];
        	tmp=son[tmp][ch];
        	fail[total]=tmp; son[cur][ch]=total;
        	diff[total]=len[total]-len[tmp]; //间隔序列
        	anc[total]=(diff[total]==diff[tmp]? anc[tmp]:tmp); //开头位置
    	}
    	last=son[cur][ch];
	}
}dt[11];
char a[12][maxn];
int n,k,ans[maxn];

void solve()
{
	//cout<<(sizeof(dt) >> 20)<
	for (int i = 1 ; i <= k ; i++) dt[i].init();
    for (int i=1; i<=n; ++i) ans[i]=inf;
    ans[1] = 1;
    for (int i=1; i<=n; ++i)
    {
    	for (int t = 1 ; t <= k ; t++){
	        dt[t].insert(a[t][i] - 'a');
	        for (int j=dt[t].last; j; j= dt[t].anc[j])
    	    {
        	    dt[t].hd[j]=i-dt[t].len[dt[t].anc[j]]-dt[t].diff[j]; //GPL存放位置
            	if (dt[t].anc[j]!=dt[t].fail[j] && ans[dt[t].hd[dt[t].fail[j]]]<ans[dt[t].hd[j]])
                	dt[t].hd[j]=dt[t].hd[dt[t].fail[j]];
            	if (ans[dt[t].hd[j]]+1<ans[i]) ans[i]=ans[dt[t].hd[j]]+1;
        	}
    	}
    }
    cout<<ans[n]<<endl;
    //for (int i = 1 ; i <= n ; i++) cout<
}
int main(){
	scanf("%d %d",&k,&n);
	rep(i,1,k) scanf("%s",a[i] + 1);
	solve();
}

E - Travelling by River

转化后题意
在线询问两点可达性
走过的边只能标号都递增或者都递减
特殊性质是只会和标号差不超过k的点连边
2e5, k <= 200

题解
巧妙的分治
任意两点可达必定经过其间长度为k的区间
那么分治,在这两个点被分治区间中点隔开的地方统计
只需要用bitset维护当前区间的点到中间k个点分别走正向边和反向边的可达性
复杂度:O(nlogn * k / 64)

#include
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const ld inf = 2e18;
const int maxn = 200020;
const ll mod = 1e9 + 7;

bitset <220> bit[18][maxn],rbit[18][maxn];
vector <int> e[maxn],re[maxn];
int n,m,k,Q,a[maxn],ans[maxn],tot;

void solve(int dep,int l,int r){
	if ( l == r ) return;
	int mid = (l + r) >> 1;
	solve(dep + 1,l,mid) , solve(dep + 1,mid + 1,r);
	int R = mid + k / 2 , L = R - k + 1;
	if ( r - l + 1 <= k ) L = l , R = r;
	for (int i = L ; i <= r ; i++){
		if ( i <= R ) rbit[dep][i][i - L] = 1;
		for ( auto v : re[i] ){
			if ( v >= l )
				rbit[dep][i] |= rbit[dep][v];
		}
	}
	for (int i = R ; i >= l ; i--){
		if ( i >= L ) bit[dep][i][i - L] = 1;
		for (auto v : e[i]){
			if ( v <= r )
				bit[dep][i] |= bit[dep][v];
		}
	}
}
void init(){
	solve(0,1,n);
}
bool query(int dep,int l,int r,int L,int R){
	if ( l == r ) return 1;
	int mid = (l + r) >> 1;
	//in this layer
	if ( L <= mid && R > mid ){
		return (bit[dep][L] & rbit[dep][R]).count();
	}
	if ( R <= mid ) return query(dep + 1,l,mid,L,R);
	return query(dep + 1,mid + 1,r,L,R);
}
bool can(int u,int v){
	if ( u == v ) return 0;
	if ( u > v ) swap(u,v);
	return query(0,1,n,u,v);
}
int main(){
	scanf("%d %d %d",&n,&m,&k);
	for (int i = 1 ; i <= m ; i++){
		int x,y;
		scanf("%d %d",&x,&y);
		e[x].pb(y) , re[y].pb(x);
	}
	init();
	scanf("%d",&Q);
	for (int i = 1 ; i <= Q ; i++){
		int x;
		scanf("%d",&x);
		if ( !tot || can(ans[tot],x) ) ans[++tot] = x;
	}
	printf("%d\n",tot);
	for (int i = 1 ; i <= tot ; i++){
		printf("%d ",ans[i]);
	}
}

你可能感兴趣的:(ACM,训练小结)