zoukankan      html  css  js  c++  java
  • P1912[NOI2009]诗人小G【四边形不等式,单调队列】

    正题

    题目链接:https://www.luogu.com.cn/problem/P1912


    题目大意

    给出\(n\)个字符串,把这些字符串依次用空格(算一个长度)连接分成若干段,若一段长度为\(x\),那么代价是\(|x-L|^P\)

    求代价和最小的方案,如果代价大于\(1e18\)则输出其他东西

    \(1\leq n\leq 10^5,1\leq L\leq 3\times 10^6,1\leq P\leq 10\)


    解题思路

    \(s_i\)表示前\(i\)个字符串的长度和加\(i\),那么有转移方程

    \[f_i=min\{f_j+|s_i-s_j-1-L|^P\} \]

    这个转移很麻烦不能直接用单调队列之类的优化,但是它满足四边形不等式
    \(w_{i,j}=|s_i-s_j-1-L|^P\),然后满足

    \[w_{i,j}+w_{i+1,j+1}\leq w_{i,j+1}+w_{i+1,j} \]

    这里就不证明了,因为证明需要用到求导。

    感谢理解的话可以发现因为有个\(abs\),所以对于一个决策来说是先下后上,而且两个决策最多只有一个交点。

    所以有决策单调性,我们用单调队列维护一个该决策和它的下一个决策的交换点\(k_i\),然后每次判断新加入的点与队尾的前一个的交换点是否会代替掉队尾即可。

    求交换点的话用二分就好了。

    时间复杂度\(O(Tn\log n)\)

    怕转移太大可以用\(long\ double\)存,因为如果很大的时候精度就不需要管了,我们只需要知道它是否超过\(1e18\)就好了。


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long double
    using namespace std;
    const int N=1e5+10;
    int T,n,L,P,p[N],k[N],q[N];
    ll f[N],s[N];
    char st[N][31];
    ll power(ll x,int b){
    	ll ans=1;
    	while(b){
    		if(b&1)ans=ans*x;
    		x=x*x;b>>=1;
    	}
    	return ans;
    }
    ll calc(int j,int i)
    {return f[j]+power(fabs(s[i]-s[j]-1-L),P);}
    int bound(int i,int j){
    	int l=i,r=n;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(calc(i,mid)<calc(j,mid))l=mid+1;
    		else r=mid-1;
    	}
    	return l;
    }
    void print(int n){
    	if(!n)return;print(p[n]);
    	for(int i=p[n]+1;i<n;i++)
    		printf("%s ",st[i]);
    	puts(st[n]);
    }
    int main()
    {
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d%d",&n,&L,&P);
    		for(int i=1;i<=n;i++){
    			scanf("%s",st[i]);
    			s[i]=s[i-1]+strlen(st[i])+1;
    		}
    		int head=1,tail=1;q[1]=0;
    		for(int i=1;i<=n;i++){
    			while(head<tail&&k[head]<=i)head++;
    			f[i]=calc(q[head],i);p[i]=q[head];
    			while(head<tail&&k[tail-1]>=bound(q[tail],i))tail--;
    			k[tail]=bound(q[tail],i);q[++tail]=i;
    		}
    		if(f[n]>1e18)puts("Too hard to arrange");
    		else printf("%lld\n",(long long)f[n]),print(n);
    		puts("--------------------");
    	}
    	return 0;
    }
    
  • 相关阅读:
    自己动手实现java数据结构(五)哈希表
    自己动手实现java数据结构(四)双端队列
    自己动手实现java数据结构(三) 栈
    自己动手实现java数据结构(二) 链表
    自己动手实现java数据结构(一) 向量
    redis学习(七)redis主从复制
    redis学习(六)redis管道
    msf之meterpreter权限绑定以及端口转发
    MSF查找提权exp
    Cobait Strike的socks与ew代理使用
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14433059.html
Copyright © 2011-2022 走看看