2
1
2016
1

【20160202考试】 总结

上午考试的题是某高校的寒假集训题。

T1 :

     题意:$S_0 = '0',S_1 = '1',S_i = S_{i-1} + S_{i-2} (i > 1)$,求一个给定的串tmp在$S_n$中的出现次数,对P取模

     题解:$O(n \times m)$的做法是很简单的,暴力合并kmp,这里就是观察到当串的长度大于tmp的长度之后,两个间隔了一个串的串,开头M个字符和结尾M个字符是一样的,所以可以分两类,每一次两步两步走的矩阵快速幂。

   

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <memory.h>


using namespace std;

int n,m,MOD;

const int MAXN = 60010;

char s[50][MAXN];
char tmp[MAXN];
char L[2][MAXN],R[2][MAXN],Mid[2][MAXN];
int ans1,ans2;
int delta[2],len[MAXN];

int fail[MAXN];
int Kmp(char *s1,char *s2) {
		int n = strlen(s1 + 1);
		int m = strlen(s2 + 1);
		memset(fail,0,sizeof fail);
		for (int i=2;i<=m;i++) {
				int last = fail[i - 1];
				while (last && (s2[last + 1] != s2[i])) last = fail[last];
				if (s2[last + 1] == s2[i]) fail[i] = last + 1;
				else fail[i] = 0;
		}
		int now = 0;
		int cnt = 0;
		for (int j=1;j<=n;j++) {
				while (now && (s2[now + 1] != s1[j])) now = fail[now];
				if (s2[now + 1] == s1[j]) now ++;
				if (now == m) cnt ++;
		}
		return cnt;
}


struct  Matrix {
		int w,h;
		int a[4][4];
		Matrix (int w_ = 0,int h_ = 0) {
				w = w_,h = h_;
				memset(a,0,sizeof a);
		}
		Matrix operator * (const Matrix ano) const {
				Matrix res = Matrix(ano.w,h);
				for (int i=1;i<=h;i++) {
						for (int j=1;j<=ano.w;j++) {
								for (int k=1;k<=w;k++) {
										res.a[i][j] += 1LL * a[i][k] % MOD * ano.a[k][j] % MOD;
										res.a[i][j] %= MOD;
								}
						}
				}
				return res;
		}
};

Matrix m0,m1,mat,ret;
void Init() {
		m0 = Matrix(3,3),m1 = Matrix(3,3);
		m0.a[1][1] = m0.a[1][2] = m0.a[2][1] = m0.a[3][3] = 1;
		m0.a[1][3] = delta[0];
		m1.a[1][1] = m1.a[1][2] = m1.a[2][1] = m1.a[3][3] = 1;
		m1.a[1][3] = delta[1];
		ret = Matrix(3,3);
		ret.a[1][1] = ret.a[2][2] = ret.a[3][3] = 1;
		mat = m1 * m0;
}

void Solve() {
		int times = n / 2;
		while (times) {
				if (times & 1) ret = ret * mat;
				mat = mat * mat;
				times >>= 1;
		}
		if (n & 1) ret = m0 * ret;
		Matrix M = Matrix(1,3);
		M.a[1][1] = ans2,M.a[2][1] = ans1,M.a[3][1] = 1;
		M = ret * M;
		printf("%d\n",M.a[1][1]);
		return ;
}
		
int main() {
		freopen("bugs.in","r",stdin);
		freopen("bugs.out","w",stdout);
		scanf("%d%d%d",&n,&m,&MOD);
		scanf("%s",tmp + 1);
		s[0][1] = '0';
		s[1][1] = '1';
		len[0] = len[1] = 1;
		int now = 1;
		while (len[now - 1] < (m > 1 ? m : m + 1)) {
				now ++;
				for (int i = 1;i <= len[now - 2];i ++) {
						s[now][i] = s[now - 2][i];
				}
				for (int i = 1;i <= len[now - 1];i ++) {
						s[now][i + len[now - 2]] = s[now - 1][i];
				}
				len[now] = len[now - 1] + len[now - 2];
		}
		ans1 = Kmp(s[now - 1],tmp) % MOD;
		ans2 = Kmp(s[now],tmp) % MOD;
		if (n <= now) {
				printf("%d\n",Kmp(s[n],tmp));
				return 0;
		}
		for (int i = 1;i <= m; i++) {
				L[0][i] = s[now - 1][i];
				L[1][i] = s[now][i];
				R[0][i] = s[now - 1][len[now - 1] - (m - i)];
				R[1][i] = s[now][len[now] - (m - i)];
		}
		for (int i=1;i<m;i++) {
				Mid[0][i] = R[0][i + 1];
				Mid[1][i] = R[1][i + 1];
		}
		for (int i = 1;i < m ;i ++) {
				Mid[0][i + m - 1] = L[1][i];
				Mid[1][i + m - 1] = L[0][i];
		}
		n -= now;
		delta[0] = Kmp(Mid[0],tmp) % MOD;
		delta[1] = Kmp(Mid[1],tmp) % MOD;
		Init();
		Solve();
}








 T2 :

       题意:给定一个长度为$N$的序列,求出最长的一个区间$[L,R]$使得$A[L] \leq A[i] \leq A[R]$对于任意$L \leq i \leq R$均成立,输出区间长度,$N \leq 10^7$

       题解:单调栈裸题

 

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <memory.h>

using namespace std;

int n;

const int MAXN = 10000010;

inline void read(int &a) {
		a = 0;char c = getchar();
		while (isspace(c)) c = getchar();
		do a = a * 10 + c - '0',c=  getchar();while (isdigit(c));
}
int a[MAXN];
int st[MAXN],tp = 0,pos[MAXN],M[MAXN];
int ans = 0 ;
int main() {
		freopen("array.in","r",stdin);
		freopen("array.out","w",stdout);
		read(n);
		for (register int i = 1;i <= n;i ++) {
				read(a[i]);
				int mini = a[i],p = i;
				while (tp && a[i] >= st[tp]) {	
						if (a[M[pos[tp]]] <= mini) mini = a[M[pos[tp]]],p = M[pos[tp]];
					    tp --;
				}
				st[++tp] = a[i],pos[tp] = i;
				M[i] = p;
				if (i - p + 1 > ans) ans = i - p + 1;
		}
		printf("%d\n",ans);
}

					

	

T3 :

      题意:给定一个图,其中每个点属于某两个极大团,极大团总数不超过$M$,点数不超过$N$,问最大匹配

      题解:首先把每个点随便放, 然后观察得到可以把每个极大团当成一个点,一个点当成一条边,则此时的一条路径可以理解为把终点和起点里面点的奇偶性取反,此时只需要求生成树森林然后在树上打标记就好了。

 

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <memory.h>
#include <vector>
#define lowbit(_x_) ((_x_) & (-(_x_)))
using namespace std;

int n,m;
const int MAXN = 100010;

int L[MAXN],R[MAXN],fa[MAXN];

int findfa(int x) {return fa[x ]== x ? x : fa[x] = findfa(fa[x]);}

struct Edge {
		int l,r,id,nxt;
} e[MAXN << 1];

int ecnt,hed[MAXN],due[MAXN];

void addedge(int l,int r,int id) {
		++ecnt,e[ecnt].l = l,e[ecnt].r =r ,e[ecnt].id = id,e[ecnt].nxt= hed[l],hed[l] = ecnt;
}

bool vis[MAXN];

int seq[MAXN],seq_id = 0,fid[MAXN];
int dep[MAXN];

void dfs(int x) {
		seq[++seq_id] = x;
		L[x] = seq_id;
		for (int i=hed[x];i;i=e[i].nxt) {
				int to = e[i].r;
				if (!vis[to]) {
						vis[to] = 1;
						dep[to] = dep[x] + 1;
						fid[to] = i;
						dfs(to);
				}
		}
		R[x] = seq_id;
}
int bel[MAXN << 1],ll[MAXN << 1],rr[MAXN << 1];
vector<int> G[MAXN];

int bit[MAXN];

void modify(int p) {for (;p <= n;p += lowbit(p)) bit[p] ^= 1;}
int query(int p) {int ans = 0;for (; p; p -= lowbit(p)) ans ^= bit[p];return ans;}

void Init() {
		for (int i=1;i<=n;i++) {
				if (!vis[i]) vis[i] = 1,dep[i] = 0,dfs(i);
		}
		for (int i=1;i<=n;i++) {
				if (due[i] & 1) G[findfa(i)].push_back(i);
		}
}

vector<int> In[MAXN];
void Solve() {
		for (int i=1;i<=n;i++) {
				for (int j=0;j<G[i].size();j ++,j ++) {
						if (G[i].size() - j - 1) modify(L[G[i][j]]),modify(L[G[i][j+1]]);
				}
		}
		for (int i=1;i<=n;i++) {
				int P = query(R[i]) ^ query(L[i] - 1);
				if (P) bel[e[fid[i]].id] ^= 1;
		}
		for (int i=1;i<=m;i++) {
				if (bel[i]) In[rr[i]].push_back(i);
				else In[ll[i]].push_back(i);
		}
		int ans =0 ;
		for (int i=1;i<=n;i++) ans += In[i].size() / 2;
		printf("%d\n",ans);
		for (int i=1;i<=n;i++) {
				for (int j=0;j<In[i].size();j += 2) {
						if (In[i].size() - j - 1) printf("%d %d\n",In[i][j],In[i][j+1]);
				}
		}
}

int main() {
		freopen("pair.in","r",stdin);
		freopen("pair.out","w",stdout);
		scanf("%d%d",&n,&m);
		for (int i = 1; i <= n; i ++) fa[i] = i;
		for (int i=1;i<=m;i++) {
				int l,r;
				scanf("%d%d",&l,&r);
				if (findfa(l) != findfa(r)) {
						fa[findfa(l)] = findfa(r);
						addedge(l,r,i);
						addedge(r,l,i);
				}
				ll[i] = l,rr[i] = r;
				due[l] ++;
		}
		Init();
		Solve();
}


Category: 未分类 | Tags: 杂题
11
11
2015
0

【BZOJ 2118】墨墨的等式 | 最短路

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
using namespace std;

int n;
long long L,R;

const int MAXN = 500010;
int a[MAXN];
long long dis[MAXN];
bool inq[MAXN];
queue<int> q;
void spfa() {
	memset(dis,-1,sizeof dis);
	dis[0] = 0;
	q.push(0);
	inq[0] = 1;
	while (q.size()) {
		int cur = q.front();
		q.pop();
		inq[cur] = 0;
		for (int i=1;i<n;i++) {
			int pr = a[i];
			int to = (cur + pr) ;
			if (to >= a[n]) to -= a[n];
			if (dis[to] < 0 || dis[to] > dis[cur] + a[i]) {
				dis[to] = dis[cur] + a[i];
				if (!inq[to]) {
					inq[to] = 1;
					q.push(to);
				}
			}
		}
	}
}

long long  calc(long long x) {
	long long ret = 0;
	for (int i=0;i<a[n];i++) {
		if (dis[i] >= 0) {
			if (dis[i] > x) continue;
			ret += (x - dis[i]) / a[n] + 1;
		}
	}
	return ret;
}


int main() {
#ifdef YJQ_LOCAL
	freopen(".in","r",stdin);
#endif
	cin>>n>>L>>R;
	for (int i=1;i<=n;i++) {
		cin>>a[i];
	}
	sort(a+1,a+n+1);
	spfa();
	cout<<calc(R) - calc(L - 1)<<endl;
}
 

题意:给定序列长为$N$的序列$X_i$,问在$[L,R]$中有多少值$V$可以被表示为$\sum_{i=1}^{N}{A_i \times X_i}$,其中$0 \leq A_i$且$A_i$为整数

 

思路:任取一个$X_p$,看用剩下的值,对于每一个$X_p$的余数,最小的可以达到的值是多少,那么对于任意比这个数大,且与这个数对于$X_p$同余的数都可以在此基础上,加上若干个$X_p$得到,前者是一个最短路的模型,统计答案的时候随便就水过了,注意long long

 

Category: 未分类 | Tags:
9
19
2015
0

【日常向】20150919小结

上午是考试,不知道是哪年的题那么水,又是一个小时AK的节奏,感觉NOIP就要挂

然后去听高一的课,还被mhy拉上去讲了一下树状数组OvO

中午去吃饭偶遇QYC,看来物理竞赛的省选还是好2333333

下午卡了一个小时的精度终于卡过了BZOJ 2135,然后就有时间来切水题了

BZOJ 1968 200B写过去的题23333

BZOJ 3998 TJOI弦论 SAM入门题

BZOJ 3252 攻略 dfs序+线段树,听说还可以用priority_queue直接水?不清楚

然后被一道题虐了智商(以为重庆的题都是傻逼题系列)

然后就开始看laekov的diary了233333

Category: 未分类 | Tags:
9
18
2015
0

【BZOJ 2135】刷题计划 #二分 + 卡精度#

题意:给n个实数,可以在任意位置加入任意大小的m个实数,希望让插入后的序列的$\sum_{i=1}^{n+m}(a[i]-a[i-1]-L)^2$最小

​$n\leq50000  m\leq10^8 |L|\leq100$

题解:

首先把式子拆开,变成 $\sum_{i=1}^{n+m}(a[i]-a[i-1])^2 - 2*a[n]*L + 2*a[1] * L + L^2 * (n+m-1)$

此时​发现后面三个东西,都不会变化(你往两边插不会使答案变小)

然后就变成了往一个序列里面插入某些数,使相邻两个数的平方和最小

我们考虑把相邻两个差为k的数中间,插入j个数,最小变成多少

 

显然应该均匀插入,于是这两个数对sum的贡献从k^2变成了k^2/(j+1)

我们考虑增量,发现从插入i个数,变成插入i+1个数,相当于是答案变小了k^2/(i*(i+1))

然后用一个priority_queue,记录增量,可以做到mlgn,但是显然是过不了的

最后发现可以二分最小的增量大小,判断是否存在m个比二分的增量大的增量,于是复杂度变成了n*二分次数

注意二分的精度相当恶心

同时还要注意,虽然往两边插入不会使答案变小,但是如果最后二分出来的增量小于L^2,可以向两边插入

也就是说我们二分的增量大小,最小是L^2

 

代码:

 

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <memory.h>
#include <stack>
#include <vector>
#include <cmath>
using namespace std;

double L;

int n,m;

const int MAXN = 50010;
double eps = 1e-14;

int sgn(double x) {
		if (abs(x) < eps) return 0;
		if (x >= eps) return 1;
		return -1;
}
double a[MAXN];
double ans = 0;
int get_sum(double k,double x) {
		if (sgn(x - k/(double) 2.0) > 0) return 0;
		int p = sqrt(k/x + 0.5);
		p ++;
		while (sgn(k / ((double) p * (double) (p+1))  - x) < 0 && p) p --;
		return p;
}
int calc(double x) {
		int ret = 0;
		for (int i=2;i<=n;i++) {
				double k = a[i] - a[i-1];
				ret += get_sum(k*k,x);
		}
		return ret;
}

double sqr(double x) {
		return x * x;
}
int main() {

		scanf("%d%d%lf",&n,&m,&L);
		for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
		for (int i=2;i<=n;i++) {
				ans += (a[i] - a[i-1] - L) * (a[i] - a[i-1] - L);
		}
		double l = L * L;
		double r = 1e9;
		if (m < 5000) eps = 1e-10;//protect from bao jing du
		while (sgn(r-l) > 0) {
				double mid = (l + r) / 2.0;
				if (calc(mid) < m) r = mid;
				else l = mid;
		}
		for (int i=2;i<=n && m;i++) {
				int p = get_sum((double) sqr(a[i] - a[i-1]),l);
				p = min(p,m);
				if (p) 
				ans = ans - sqr(a[i] - a[i-1]) + (double) sqr(a[i] - a[i-1]) / ((double) (p + 1)) + L * L * p;
				m -= p;
		}
		printf("%.3lf\n",ans);
}

 

 

Category: 未分类 | Tags:
8
6
2015
0

【行记】SCOI2015

  人生中的第一次省选,也就这个样子咯。
 
 就好像做了一场梦一样,2015年的省选就过去了。平心而论,我确实是想进省队的,但是NOIP跪成狗的我并没有什么机会,或者说,明明有机会却自己放过去了。
 
省选前几天,UESTC把我们拐卖到一个鬼畜的基地里面训练的几天,还考了几场试,每次都在前五(那时就感觉人品已经掉光了)。
每天晚上都看着同屋的xsp在看电视剧,然后我们这个房间里面就是欢歌笑语,但却能明显感受到高二的房间里面,是一种忧伤的氛围。
毕竟谁也不能说自己已经稳进省队了。
 
DAY1那天前的晚上,我感冒了,还挺严重的,几乎说不出话来,吃了点药就睡觉了,想着反正也不是自己的主赛季,睡得竟然挺踏实的
第二天早上起来匆匆吃了顿早饭,就是去考试了。UESTC的清水河校区挺好的,就是稍微大了点,让我们多了一点紧张的时间。
到了比赛的地方,看到大家都在那里围着,开着各种各样的玩笑,立着花式flag,一种已经考完的错觉
看到题的那一刹那,总感觉是到了NOI的赛场上,肾上腺素迅速分泌的感觉还是挺6的
第一题看了一会儿没什么想法,然后YY了一个分成把一行一列拆成两个点的网络流,后来发现就是个SB东西,完全没法做。再想感觉可以二分,然后码到一半感觉出鬼了,当时一下子就慌了。花了10分钟来整理思绪,之后又看了一下发现仍然有二分性,然后就码码码,暴力拍的飞起来。
第二题第一眼以为是什么奇怪的数据结构,后来发现有个显然的贪心,然后发现是道水题,然而我并有写倍增,只写了一个分块。果然还是太弱了
第三题一看就是半平面交+面积,发现没写过直接弃疗了,写了个1/n骗了10分
 
出来感觉是210的分数,结果发现只有150,第二题炸了。
然而这么水的题150居然排在第六名,看到高二的学长学姐们各种看错题,各种发挥失误,也是挺心塞的。
晚上出去吃饭,陪xsp喝了点啤酒,看着他那么难过还是挺伤心的,还好他才高一,有的是时间去调整状态
 
之后就是DAY2,可能是心态不一样了吧,感觉可以进队之后睡也没睡好,基本上是浑浑噩噩地就开了机子开始看题。
第一题感觉就是道树形dp,看了一眼发现好像是n^2的状态,转移还是均摊O(1)的,然后发现总的状态实际上只有nlgn个,开始码码码,但是毕竟当时的我代码能力就跟渣一样,最后以为码对了,结果边界挂了全部炸了
第二题一开始感觉是线段树裸题,就各种花式做,后来发现题看错了,但是好像还是线段树,但是好像来不及了OvO,暴力弃疗。
最后半个小时看第三题,woc竟然这么水,难怪他们写得飞起来。特别简单的链剖+主席树,果然就是钱桥拿来骗钱的,感觉自己瞬间不能进队了,随便写了个链剖弃疗了。
 
出来的时候估分100+20+60,似乎还有180,感觉挺不错的,听说今天的分应该比昨天低,那岂不是可以进队了?
等成绩的时候各种拖,直到UESTC的人出来,拿着一张单子,然后所有人都涌了上去,tyh跟我说我50,瞬间就蒙了。
 
看到第一题全是R的时候,我的内心几乎是崩溃的。
去申诉,发现算法和std一模一样,除了std知道那棵树不一定是满二叉树,而我不知道以外。
 
然后就滚粗了。或者说,我离进队就差那个满二叉树和完全二叉树的距离,但也没有什么办法,如果真的进队了,抢了别人的名额,也许我自己也不知道该怎么面对了吧
Category: 未分类 | Tags:
8
4
2015
0

【BZOJ】3170 松鼠聚会 | 数据结构::线段树?

题解:

    (此题据说是TJ省选题?不是提高组么)

     先转45°,然后顺手就可以A了,我用的线段树统计的答案,统计的方法是直接用max(abs(x[i]-x[j]),abs(y[i]-y[j]))的公式,枚举一个点作为中点,然后把平面分成4块,分块统计(妥了),注意边界问题

 

#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
#include <cstring>
#include <cstdlib>
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
using namespace std;
const int MAXN = 100010;
int n;
struct NODE {
		int l,r,sum;
		long long sumx,sumy;
} seg[MAXN << 2];

struct POINT {
		int x,y,lab,a,b;
		void read() {
				scanf("%d%d",&x,&y);
				a = y-x;
				b = y+x;
		}
} pnts[MAXN];

int pos[MAXN];

struct OPT {
		int v,lab;
		bool operator < (const OPT another) const {
				return v < another.v;
		}
} opt[MAXN];
int pc(0);
void Uni() {
		sort(opt+1,opt+n+1);
		for (int i=1;i<=n;i++) {
				if ((opt[i].v != opt[i-1].v) || (i == 1)) pos[opt[i].lab] = ++pc;
				else pos[opt[i].lab] = pc;
		}
}

bool cmp(POINT p1,POINT p2) {
		return p1.b < p2.b;
}

void build(int x,int l,int r) {
		seg[x].l = l;
		seg[x].r =r ;
		seg[x].sumx= seg[x].sumy = seg[x].sum = 0;
		if (l == r) return;
		int mid = (l + r)>> 1;
		build(x << 1,l,mid);
		build((x << 1) + 1,mid+1,r);
}


void modify(int x,int p,int a,int b) {
		if (seg[x].l == seg[x].r) {
				seg[x].sum ++;
				seg[x].sumx += a;
				seg[x].sumy += b;
				return;
		}
		int mid = (seg[x].l + seg[x].r) >> 1;
		if (p <= mid) modify(x << 1,p,a,b);
		else modify((x << 1) + 1,p,a,b);
		seg[x].sum = seg[x << 1].sum + seg[(x << 1) + 1].sum;
		seg[x].sumx = seg[x << 1].sumx + seg[(x << 1) + 1].sumx;
		seg[x].sumy = seg[x << 1].sumy + seg[(x << 1) + 1].sumy;
}

void merge(NODE &s,NODE s1,NODE s2) {
		s.sum = s1.sum + s2.sum;
		s.sumx = s1.sumx + s2.sumx;
		s.sumy = s1.sumy + s2.sumy;
}
NODE query(int x,int l,int r) {
		if (seg[x].l == l && seg[x].r == r) return seg[x];
		int mid = (seg[x].l + seg[x].r) >> 1;
		if (r <= mid) return query(x << 1,l,r);
		if (l > mid) return query((x << 1) + 1,l,r);
		NODE s;
		merge(s,query(x << 1,l,mid),query((x << 1) + 1,mid+1,r));
		return s;
}

long long ret[MAXN];
int main() {
		scanf("%d",&n);
		for (int i=1;i<=n;i++) {
				pnts[i].read();
				pnts[i].lab = i;
				opt[i].v = pnts[i].a;
				opt[i].lab = i;
		}
		Uni();
		sort(pnts+1,pnts+1+n,cmp);
	//	for (int i=1;i<=n;i++) {
	//			printf("%d %d\n",pnts[i].x,pnts[i].y);
	//	}
		build(1,0,pc);
		for (int i=1;i<=n;i++) {
				int cur = pnts[i].lab;
				int p = pos[cur];
				NODE s = query(1,p,pc);
				ret[cur] += (long long) pnts[i].x * (long long)s.sum - s.sumx;
				s = query(1,0,p-1);
				ret[cur] += (long long) pnts[i].y * (long long)s.sum - s.sumy;
				modify(1,p,pnts[i].x,pnts[i].y);
		}
		build(1,0,pc);
		for (int i=n;i;i--) {
				int cur = pnts[i].lab;
				int p = pos[cur];
				NODE s = query(1,0,p-1);
				ret[cur] +=  s.sumx - (long long) pnts[i].x * (long long)s.sum;
				s = query(1,p,pc);
				ret[cur] += s.sumy - (long long) pnts[i].y * (long long)s.sum;
				modify(1,p,pnts[i].x,pnts[i].y);
		}
		long long ans = ret[1];
		for (int i=2;i<=n;i++) {
				if (ret[i] < ans) {
						ans = ret[i];
				}
		}
		printf(LL "\n",ans);
}

 

Category: 未分类 | Tags:
8
4
2015
0

【BZOJ】4216 Pig | 杂题

#include <cstdio>
#include <algorithm>
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
#define lowbit(x) ((x) & (-(x)))
using namespace std;
int n,m,flag;
const int MAXN = 500010;
int bit[MAXN];
long long buf[MAXN / 20];
long long ask(int p) {
	long long ret = 0;
	int bs = p / 20;
	for (int i=1;i<=p % 20;i++) {
		ret += bit[20 * bs + i];
	}
	return buf[bs] + ret;
}	
long long query(int l,int r) {
	return ask(r) - ask(l-1);
}
int main() {
#ifdef YJQ_LOCAL
	freopen(".in","r",stdin);
#endif
	scanf("%d%d%d",&n,&m,&flag);
	long long cur = 0;
	for (int i=1;i<=n;i++) {
		int v;
		scanf("%d",&v);
		cur += v;
		bit[i] = v;
		if (i % 20 == 0) buf[i / 20] = cur;
	}
	long long lastans = 0;
	while (m--) {
		long long l,r;
		scanf(LL "" LL, &l, &r);
		if (flag) {
			l ^= lastans;
			l = l % n + 1;
			r ^= lastans;
			r = r % n + 1;
		}
		if (l > r) swap(l,r);
		lastans = query(l,r);
		printf(LL "\n",lastans);
		lastans = abs(lastans);
	}
} 

题解:

       (一开始我还以为是树状数组),题意就是给一个序列N,每次询问一段区间里面所有数的和。

         开始秒写树状数组(智商下降),然后发现T了OvO,秒改前缀和,发现MLE了←_←

         说着就陷入了纠结:总共3MB内存,然而前缀和全开long long要4MB,开int又要溢出。

         蒟蒻的我开始向zhonghaoxi求救,被D了一番,然后钟神教我写分块OvOOvOOvO

         怎么就没想到呢。

Category: 未分类 | Tags:
8
3
2015
0

【BZOJ】3333 排队计划 | 数据结构::线段树

题解:

    (被无良的出题人拿来骗钱的题)o( ̄ヘ ̄o)

     此题比较水,首先可以发现一个性质:一个位置改过一次之后,再改这个位置对答案没有影响。之后又发现了一个玄妙的东西:每个没有被改过的位置,它在第一次被重排的时候对答案的影响不会变化。

     于是就变成了求每次修改时候,被影响的没被影响过的位置,然后就是一个线段树#include <cstdio>

#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <memory.h>
#include <stack>
#include <vector>
#define lowbit(x) ((x) & (-(x)))
#ifdef YJQ_LOCAL
#define LL "%I64d"
#else
#define LL "%lld"
#endif
using namespace std;
int n,m,pcnt;
const int MAXN = 500010;
struct OPT {
        int v,pos;
} opt[MAXN];
vector<int> g[MAXN];
int siz[MAXN];
bool cmp(OPT o1,OPT o2) {
        return o1.v < o2.v;
}
int lab[MAXN],a[MAXN];
void Uni() {
        for (int i=1;i<=n;i++) {
                opt[i].v = a[i];
                opt[i].pos = i;
        }
        sort(opt+1,opt+n+1,cmp);
        for (int i=1;i<=n;i++) {
            if (opt[i].v == opt[i-1].v) lab[opt[i].pos] = pcnt;
            else lab[opt[i].pos] = ++pcnt;
        }
}   
int low[MAXN],bit[MAXN],pr[MAXN];
long long ans = 0;
bool vis[MAXN];
void modify_bit(int p,int v) {
        for (;p<=pcnt;p+=lowbit(p))
               bit[p] += v;
}
 
int query_bit(int p) {
        int ret= 0;
        for (; p; p-=lowbit(p)) ret += bit[p];
        return ret;
}
 
struct NODE {
        int l,r,maxi;
} seg[MAXN << 2];
 
void build(int x,int l,int r) {
        seg[x].l = l;
        seg[x].r= r;
        if (l == r) {
                seg[x].maxi = pr[l];
                return;
        }
        int mid = (l + r) >> 1;
        build(x << 1,l,mid);
        build((x << 1) ^ 1,mid+1,r);
        seg[x].maxi = max(seg[x << 1].maxi,seg[(x << 1) + 1].maxi);
}
 
void modify(int x,int p,int v) {
        if (seg[x].l == seg[x].r) {
                seg[x].maxi = v;
                return;
        }
        int mid = (seg[x].l + seg[x].r) >> 1;
        if (p <= mid) modify(x << 1,p,v);
        else modify((x << 1) + 1,p,v);
        seg[x].maxi=  max(seg[x << 1].maxi,seg[(x << 1) ^ 1].maxi);
}
 
void solve(int x,int p) {
        while (siz[x]) {
                int cur = g[x][siz[x]-1];
                if (cur < p) {
                        modify(1,x,cur);
                        return;
                }
                ans -= low[cur];
                vis[cur] = 1;
                siz[x] --;
        }
        modify(1,x,0);
}
 
 
void find(int x,int p) {
        if (seg[x].l == seg[x].r) {
                solve(seg[x].l,p);
                return;
        }
        if (seg[x << 1].maxi >= p) find(x << 1,p);
        if (seg[(x << 1) + 1].maxi >= p) find((x << 1) + 1,p);
}
 
void Find(int x,int l,int r,int p) {
        if (seg[x].l ==l  && seg[x].r == r) {
                find(x,p);
                return;
        }
        int mid = (seg[x].l + seg[x].r) >> 1;
        if (r <= mid) Find(x << 1,l,r,p);
        else if (l > mid) Find((x << 1) ^ 1,l,r,p);
        else {
                Find(x << 1,l,mid,p);
                Find((x << 1) ^ 1,mid+1,r,p);
        }
        return;
}
int main(){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) {
                scanf("%d",&a[i]);
        }
        Uni();
        for (int i=n;i;i--) {
                int cur = lab[i];
            //  printf("%d %d\n",i,cur);
                low[i] = query_bit(cur-1);
                modify_bit(cur,1);
                ans = ans + low[i];
                if (!pr[cur]) pr[cur] = i;
        }
        printf(LL "\n",ans);
        build(1,1,pcnt);
        for (int i=1;i<=n;i++) {
                g[lab[i]].push_back(i);
                siz[lab[i]] ++;
        }
        while (m--) {
                int p;
                scanf("%d",&p);
                if (vis[p]) {
                        printf(LL "\n",ans);
                        continue;
                }
                Find(1,1,lab[p],p);
                printf(LL "\n",ans);
        }
}
Category: 数据结构 | Tags:
8
3
2015
1

【BZOJ】1497 NOI2006最大获利 | 网络流

题解:

       一个不能更裸的最小割。先假设所有的人都被选中,然后就是割掉一些边(割边的意义,对于中转站来说是修建中转站,对于人群来说是放弃这个人群的收益),使得图合法。最后就是总答案-最大流。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#define INF 1000000007
using namespace std;
int n,m;
const int MAXN = 60010;
struct EDGE {
	int l,r,c;
	EDGE (int _l = 0,int _r = 0,int _c = 0) {
		l = _l;
		r = _r;
		c = _c;
	}
} e[MAXN << 3];
int ecnt  =0;
vector<int> g[MAXN];

void addedge(int l,int r,int c) {
	e[ecnt] = EDGE (l,r,c);
	g[l].push_back(ecnt);
	ecnt++;
}
void adde(int l,int r,int c) {
	addedge(l,r,c);
	addedge(r,l,0);
}
int ans;
int src = 0,sink;

int dis[MAXN];
queue<int> q;
bool bfs() {
	q.push(src);
	for (int i=src;i<=sink;i++) {
		dis[i] = INF;
	}
	dis[src] = 0;
	while (!q.empty()) {
		int cur = q.front();
		q.pop();
		for (int i=0;i<g[cur].size();i++) {
			int add = g[cur][i];
			int to = e[add].r;
			if (dis[to] == INF && e[add].c) {
				dis[to] = dis[cur] + 1;
				q.push(to);
			}
		}
	}
	return dis[sink] != INF;
}

int dfs(int cur,int flow) {
	if (cur == sink) return flow;
	int ret = flow;
	for (int i=0;i<g[cur].size() && ret;i++) {
		int add = g[cur][i];
		int to = e[add].r;
		if (dis[to] == dis[cur] + 1 && e[add].c) {
			int delta = dfs(to,min(ret,e[add].c));
			ret -= delta;
			e[add].c -= delta;
			e[add^1].c += delta;
		}
	}
	return flow - ret;
}

int main() {
#ifdef YJQ_LOCAL
	freopen(".in","r",stdin);
#endif
	scanf("%d%d",&n,&m);
	sink = n + m + 1;
	for (int i=1;i<=n;i++) {
		int v;
		scanf("%d",&v);
		adde(src,i,v);
	}
	for (int i=1;i<=m;i++) {
		int cur = i + n;
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		adde(cur,sink,c);
		ans += c;
		adde(a,cur,INF);
		adde(b,cur,INF);
	}
	while (bfs()) ans -=dfs(src,INF);
    printf("%d\n",ans);
}	
Category: 网络流 | Tags:
8
3
2015
5

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com