【NOI六连测】[D1T2] 过河

题目传送门:http://pan.baidu.com/s/1kVjNeeV
离线版题目:http://paste.ubuntu.com/18247944/
数据传送门:http://pan.baidu.com/s/1nvGnd8H

这个题目,第一眼看到的时候,一脸懵逼
后来仔细想了一想,成了二脸懵逼QAQ
于是写了30分的暴力,还好没写挂……

后来听题解,感觉很神
首先是结论:最优解时,所有的F(Xy)的斜率相等
这个按照原教的说法:
如果斜率不等,那么将斜率较低的河流的时间分一点给斜率较高的河流
我们就可以走得更远!
还是比较显然的吧!但是像我这种沙茶在考试的时候肯定想不出来QAQ
接下来就是怎么让斜率一样了。
原教又给了一个非常神的办法:
根据F’(Xy)的表达式可以得到,t随F’(Xy)的增加而减少,所以我们可以二分统一的斜率,然后判一判时间知否足够。
详细的推导如下:
\(
\begin{array}{l}
\partial (({V_i} + \sqrt {{u^2} – \frac{{W_i^2}}{{{t^2}}})} \cdot t) = \partial ({V_i} \cdot t) + \partial (\sqrt {{u^2} \cdot {t^2} – W_i^2} )\\
= {V_i} + \partial ({u^2} \cdot {t^2}) \cdot \frac{{0.5}}{{\sqrt {{u^2} \cdot {t^2} – W_i^2} }} = {V_i} + \frac{{{u^2} \cdot t}}{{\sqrt {{u^2} \cdot {t^2} – W_i^2} }}
\end{array}
\)
不妨设\(k = {V_i} + \frac{{{u^2} \cdot t}}{{\sqrt {{u^2} \cdot {t^2} – W_i^2} }}\)
则有\(\begin{array}{l}
{u^4} \cdot {t^2} = {(k – {V_i})^2} \cdot ({u^2} \cdot {t^2} – W_i^2)\\
{t^2} \cdot ({u^4} – {(k – {V_i})^2} \cdot {u^2}) = – {(k – {V_i})^2} \cdot W_i^2\\
t = \sqrt {\frac{{{{(k – {V_i})}^2} \cdot W_i^2}}{{{{(k – {V_i})}^2} \cdot {u^2} – {u^4}}}}
\end{array}\)
所以t随k的增大而减小,且\(k \in (\max ({V_i}) + u, + \infty ]\)
之后代码就没有什么难度啦!

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#define LD long double
using namespace std;

const int MAXN = 50+9;
const LD EPS = 1e-15;

int n;
LD v[MAXN],w[MAXN],ans[MAXN];
LD disx,MXv,u,t;

inline LD cal(LD k){
	LD ret = 0; 
	for (int i=1;i<=n;i++){
		LD r = (k-v[i])*(k-v[i]);
		LD t = sqrt(r*w[i]*w[i]/(r*u*u-u*u*u*u));
		ans[i] = t; ret += t;
	}
	return ret;
}

inline double GetAns(){
	LD ret = 0;
	for (int i=1;i<=n;i++){
		LD tmp = sqrt(u*u*ans[i]*ans[i]-w[i]*w[i]);
		ret += tmp+v[i]*ans[i];
	}
	return (double)sqrt(ret*ret+disx*disx);
}

int main(){
	freopen("river.in","r",stdin);
	freopen("river.out","w",stdout);
	scanf("%d",&n); cin>>u>>t;
	for (int i=1;i<=n;i++) cin>>w[i]>>v[i],
		disx+=w[i], MXv=max(MXv,v[i]);
	if (disx/u > t) {printf("-1\n");exit(0);}
	
	LD l=u+MXv,r=1e12,mid;
	while (r-l > EPS){
		mid = (l+r)/2.0;
		if (cal(mid) < t) r = mid;
		else l = mid;
	} 
	
	printf("%.3lf\n",GetAns());
	for (int i=1;i<=n;i++) printf("%.3lf ",(double)ans[i]);
	return 0;
} 

另外,此题卡精度QAQ,long double的EPS都得开到1e-15

【NOI六连测】[D1T1] 比赛

题目传送门:http://pan.baidu.com/s/1kVjNeeV
离线版题目:http://paste.ubuntu.com/18236489/
数据传送门:http://pan.baidu.com/s/1kUR6kTL

哎呀,绍兴一中的NOIP模拟题,第一眼拿到都一脸懵逼QAQ
想了一会儿发现,不就是总方案数减掉一个三位偏序吗?这个我会!
于是开开心心写完BIT+动态建点的线段树。然后小数据对拍,欸!居然没错 (~ ̄▽ ̄)→))* ̄▽ ̄*)o
然后一跑大数据,哎呀,空间简直炸的飞起! 然后再接下来的一个半小时里一直想办法压空间。
什么unsign short配合char之类的都用上了,然而还是不行QAQ,再加上时间复杂度也不一定过得去,于是弃疗

测完程序,发现CDQ+归并就可以卡过QAQ,然而不会CDQ,于是下午学了一学,还是比较好学的。
然而这还不是高潮!高潮在:这货可以降到一维,然后O(nlogn)轻松过 QAQ
对于任意一个符合条件的(x,y)只会有一种情况(在把A,B,C看成一样的情况下):

A[x] < A[y] 
B[x] > B[y]
C[x] > C[y]

然后不难发现,如果我们把[A,B]&[A,C]拿出来统计一次逆序对的话,这货相当于每一个符合要求的(x,y)被算了2次
于是,跑三遍一维逆序对就好了QAQ 这个能算简单的容斥吗?

BIT + 线段树:http://paste.ubuntu.com/18236197/
CDQ分治:http://paste.ubuntu.com/18236240/
逆序对虐场代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;

const int MAXN = 200000+9;

int n,a[MAXN],b[MAXN],c[MAXN],ord[MAXN];
LL vout;

namespace Fenwick_Tree{
	#define BIT Fenwick_Tree
	#define low(x) (x&(-x))
	int arr[MAXN],sum;
	
	inline void init(){
		memset(arr,0,sizeof(arr));
		sum = 0;}
	
	inline void modify(int pos){
		for (int i=pos;i<=n;i+=low(i))
			arr[i] += 1; sum++;
	} 
	
	inline int query(int pos){
		int ret = 0;
		for (int i=pos;i;i-=low(i))
			ret += arr[i];
		return sum-ret;
	}
};

inline void itv(int *A, int *B){
	BIT::init();
	for (int i=1;i<=n;i++) ord[A[i]] = i;
	for (int j=1,i=ord[j];j<=n;i=ord[++j])
		vout += BIT::query(B[i]),
		BIT::modify(B[i]);
}

int main(){
	freopen("contest.in","r",stdin);
	freopen("contest.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
	itv(a,b); itv(b,c); itv(a,c); 
	printf("%lld\n",vout/2LL);
	return 0;
}

另外,这题做的时候,发现对于小的结构体,貌似直接排序比排指针还要快一点QAQ
可能是因为后期指针寻址也要花时间吧!所以以后CDQ就直接结构体写了吧! 有图有真相:
struct_iterator

【POJ 3693】Maximum repetition substring

相关链接

题目传送门:http://poj.org/problem?id=3693
数据生成器:http://paste.ubuntu.com/18156993/

题目大意

给定长度为$n(n \le 10^5)$,问循环次数最多的子串循环多少次
请输出字典序最小的解

解题报告

这是SA的 论文题 + 神题
而且SAM貌似不可捉的样子 (╯-_-)╯╧╧
求符合要求的串已经是身心具疲,这题还要字典序最小,TM恶心到不行 ( ▼-▼ )

还是说一说怎么求符合要求的串吧:
枚举符合要求的串的长度L,那么符合要求的串一定在s[1],s[1+L],s[1+2*L]…..等位置出现(跟今年APIO的交互题有点像)
于是我们就直接height[] + ST表搞定整节循环的部分,这个地方和KMP差不多
只与前面有多少相同的,看起来不好求,但实际上如果对于答案有贡献,那么一定是补足循环节

然后是如何让字典序最小:
一般的题目没这题这么恶心,这题尤其恶心! MD再掀一次桌子都无法压抑内心的悲伤 (╯-_-)╯╧╧
这个好像只能用SA数组从小到大枚举,然后判断
哎呀,考场上要是遇到这个题,一定做不对QAQ

Code

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 200000+9;
const int SIGMA_SIZE = 26;

char pat[MAXN];

namespace Suffix_Array{
	#define SA Suffix_Array
	int sa[MAXN],height[MAXN],t1[MAXN],t2[MAXN];
	int bot[MAXN],*tmp,*rank,m,n;
	int Tm,len[MAXN],TT,cnt;
	
	namespace Sparse_Table{
		#define ST Sparse_Table
		int MN[MAXN][20];
		
		inline void build(){
			for (int i=1;i<=n;i++) memset(MN[i],0,sizeof(MN[i]));
			for (int i=1;i<=n;i++) MN[i][0] = height[i];
			for (int i=1;i<18;i++){
				for (int j=1;j<=n;j++)
					MN[j][i] = min(MN[j][i-1],MN[j+(1<<(i-1))][i-1]);
			}
		}
		
		inline int query(int l, int r){
			if (r < l) swap(l, r); l++;
			if (l < 1) return -1;
			else {
				int mid=(l+r)/2,k=0;
				while (l+(1<<k)<=mid) k++;
				return min(MN[l][k],MN[r-(1<<k)+1][k]);
			}
		}
	};
	
	inline void GetHeight(){
		for (int i=1;i<=n;i++) if (rank[i] > 1){
			int sta = max(0, height[rank[i-1]]-1);
			int p1 = i+sta, p2 = sa[rank[i]-1]+sta;
			while (pat[p1++]==pat[p2++]) sta++;
			height[rank[i]] = sta;
		}
	}	
	
	inline void build(){
		memset(bot,0,sizeof(bot));
		memset(height,0,sizeof(height));
		tmp = t1; rank = t2;
		n = strlen(pat+1); m = SIGMA_SIZE;
		
		for (int i=1;i<=n;i++) bot[tmp[i]=pat[i]-'a'+1]++;
		for (int i=2;i<=m;i++) bot[i] += bot[i-1];
		for (int i=1;i<=n;i++) sa[bot[tmp[i]]--] = i;
		rank[sa[1]] = m = 1;
		for (int i=2;i<=n;i++) rank[sa[i]]=(tmp[sa[i]]==tmp[sa[i-1]])?m:++m;
		
		for (int k=1;k<=n;k*=2){ int T=0;
			for (int i=n-k+1;i<=n;i++) tmp[++T] = i;
			for (int i=1;i<=n;i++) if (sa[i]>k) tmp[++T] = sa[i]-k;
			for (int i=1;i<=m;i++) bot[i] = 0;
			for (int i=1;i<=n;i++) bot[rank[i]]++;
			for (int i=2;i<=m;i++) bot[i] += bot[i-1];
			for (int i=n;i;i--) sa[bot[rank[tmp[i]]]--] = tmp[i];
			
			swap(rank, tmp);
			rank[sa[1]] = m = 1;
			for (int i=2;i<=n;i++) if (tmp[sa[i-1]]==tmp[sa[i]] && tmp[sa[i-1]+k]==tmp[sa[i]+k]) rank[sa[i]] = m; else rank[sa[i]] = ++m; if (m >= n) break;
		}
		
		GetHeight();
		ST::build();
	}
	
	inline void solve(){
        Tm = 1; 
        for (int k=1;k<=n;k++){
            for (int i=1;i<=n;i+=k){ 
				int sta = ST::query(rank[i],rank[i+k]),T = sta / k + 1; 
				if (sta % k && ST::query(rank[i-k+sta%k],rank[i+sta%k]) >= k-sta%k) T++;
                if (T > Tm){Tm = T; len[cnt=1] = k;}
                if (T == Tm) {len[++cnt] = k;}
            } 
        }
        if (Tm==1) printf("Case %d: %c\n",++TT,pat[sa[1]]);
        else {
            for (int i=1;i<=n;i++) for (int j=1;j<=cnt;j++) {
				if (ST::query(i,rank[sa[i]+len[j]]) >= (Tm-1)*len[j]) {
                    pat[sa[i]+Tm*len[j]] = 0;
                    printf("Case %d: %s\n",++TT,pat+sa[i]); 
                    return;
                }   
			}
        }
    }
};

int main(){
	while (scanf("%s",pat+1) && pat[1] != '#'){
		SA::build();
		SA::solve();
	}
	return 0;
}