这是一个句子测试

12
27
2015
0

BZOJ3295【Cqoi2011】动态逆序对

【题目描述】

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1
样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

 

N<=100000 M<=50000

 

Source

【题解】

终于把要刷的su套su给A掉了。撒花~~~~~~

那么这道题该怎么做呢?

编号为i的数产生的逆序对数是1~i-1中比a[i]大的数的个数和i+1~n中比a[i]小的数的个数。

那么只要先求出整个序列的逆序对数,然后求出每个数产生的逆序对数。

显然发现:有些数已经被删去了,那么就用树状数组套线段su来维护删去的数就阔以辣!

然而窝一开始在直接维护还剩下的点(好写啊),然后被卡内存了。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
const int M=100010;
int n,m,i,x,cnt;
ll ans;
int a[M],pos[M],now[M],now1[M],t[M];
struct node{int l,r;ll w;}tree[M*50];
void insert(int x,int l,int r,int p,int t){
	tree[p].w+=t;
	if (l==r)return;
	int mid=l+r>>1;
	if (x<=mid){
		if (!tree[p].l)tree[p].l=++cnt;
		insert(x,l,mid,tree[p].l,t);
	}else{
		if (!tree[p].r)tree[p].r=++cnt;
		insert(x,mid+1,r,tree[p].r,t);
	}
}
void update(int x,int p,int q){
	for (int i=x;i<=n;i+=i&-i)insert(p,1,n,i,q);
}
ll query(int x,int y,int l,int r,int p){
	if (x==l&&y==r)return tree[p].w;
	int mid=l+r>>1;
	if (y<=mid)return query(x,y,l,mid,tree[p].l);
		else if (x>mid)return query(x,y,mid+1,r,tree[p].r);
			else return query(x,mid,l,mid,tree[p].l)+query(mid+1,y,mid+1,r,tree[p].r);
}
ll Query(int x,int y,int l,int r){
	ll ans=0;
	if (l>r)return 0;
	for (int i=y;i;i-=i&-i)ans+=query(l,r,1,n,i);
	for (int i=x-1;i;i-=i&-i)ans-=query(l,r,1,n,i);
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	cnt=n;
	for (i=1;i<=n;i++){
		scanf("%d",&a[i]);
		pos[a[i]]=i;
		for (int j=a[i];j;j-=j&-j)now[i]+=t[j];
		now[i]=i-1-now[i];
		ans+=now[i];
		for (int j=a[i];j<=n;j+=j&-j)t[j]++;
	}
	memset(t,0,sizeof t);
	for (i=n;i;i--){
		for (int j=a[i];j;j-=j&-j)now1[i]+=t[j];
		for (int j=a[i];j<=n;j+=j&-j)t[j]++;
	}
	for (i=1;i<=m;i++){
		scanf("%d",&x);
		int p=pos[x];
		printf("%lld\n",ans);
		ans-=now[p]+now1[p]-Query(1,p-1,x+1,n)-Query(p+1,n,1,x-1);
		update(p,a[p],1);
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags: 树套树
12
25
2015
0

Contest20151224滚粗记

平安夜做摸你赛真是非常兹磁呦~

谢天谢地,窝萌终于不考JSOI辣!~

然并卵。。。。。由于题目太丧病,决定不订正辣!

要题目戳这

要题解戳这

T1

一看就不是很可做的样子!!

如果连20分都不会打就搞笑了。。。。。

于是推了一发,搞出了一个Σ。

接着发现好像只会直接枚举。。。

那就枚举好辣!(顺便YY了40分的傻逼蒜法)

T2

一看就是一道贪心乱搞题!!

那么就打暴力好辣!(然而利用的贪心性质还是错的~)

然后拿了18分兹磁呦~

T3

Gold_7说预处理要计蒜几何!!!

那么就弃疗吧!

值得一提的是std300行(什么窝维修数列也才打了200行)

 

Category: 蒟蒻的滚粗记 | Tags:
12
23
2015
0

论蒟蒻的花样作死之树状数组

问:你这个乡村学校的小学生,为什么要学劼劼劼呢?

答:因为窝一下做了太多的树状数组,懒得一篇一篇写了。

问:既然线段树可以代替树状数组,那么为什么要树状数组呢?

答:树状数组好写啊Orz。

问:那我可以不看冗长的介绍过程吗?

答:如果机房的门要关了或者你急着要泡妹子,请跳至最后一目。

【简介】

首先普通的一维树状数组就不说了,如果你不知道,戳这

那么窝萌阔以看到:普通的树状数组支持单点修改、片段和。

显然树状数组是解决逆序对问题的最好方法。

某大神表示阔以用树状数组来维护区间最值,不明觉厉~

inline void init()  
{  
    CLR(arr,0);  
    for(int i=1;i<=N;++i)  
        for(int j=i;j<=N&&arr[j]<num[i];j+=lowbit(j))  
            arr[j]=num[i];  
}  
inline int query(int L,int R)  
{  
    int res=0;  
    for(--L;L<R;){  
        if(R-lowbit(R)>=L){res=max(res,arr[R]);R-=lowbit(R);}  
        else{res=max(res,num[R]);--R;}  
    }  
    return res;  
}  
inline void update(int x,int val)  
{  
    int ori=num[x];  
    num[x]=val;  
    if(val>=ori)  
        for(int i=x;i<=N&&arr[i]<val;i+=lowbit(i))  
            arr[i]=val;  
    else{  
        for(int i=x;i<=N&&arr[i]==ori;i+=lowbit(i))  
        {  
            arr[i]=val;  
            for(int j=lowbit(i)>>1;j;j>>=1)  
                arr[i]=max(arr[i],arr[i-j]);  
        }  
    }  
}  

那么树状数组可不可以支持区间修改呢?

 

让窝萌先来解决一个子问题:维护一个序列,支持区间修改、单点求值。

那么差分,令d[i]=a[i]-a[i-1],d[1]=a[1],那么显然[tex]a[i] = \sum\limits_{j = 1}^n {d[j]}[/tex]

修改区间[l,r],则可以通过前缀和得到:d[l]+=k,d[r+1]-=k。

 

那么要求区间和怎么办呢?

通过简单的推导可以得到:

[tex]\sum\limits_{i = 1}^x {a[i]}  = \sum\limits_{i = 1}^x {d[i]*(x - i + 1) = (x + 1)*\sum\limits_{i = 1}^x {d[i]}  - \sum\limits_{i = 1}^x {d[i]*i} }[/tex]

那么再开一个树状数组来维护d[i]*i就阔以辣!

 

接着看二维树状数组。

显然根据树状数组的定义再加一维就阔以了。

即令tree[i][j]为矩阵[i-lowbit(i)+1,j-lowbit(j)+1]~[i,j]的片段和。

对于二维树状数组的区间修改和求和,可以通过二维前缀和的性质推出。

这里的d[i]有另一个定义,表示i~n这一段的都加上d[i]。

那么扩展到二维,d[i][j]表示矩阵[i,j]~[n,m]的都加上d[i][j]。

修改矩阵[x1,y1]~[x2,y2],那么d[x1][y1]+=k,d[x1][y2+1]-=k,d[x2+1][y1]-=k,d[x2+1][y2+1]+=k。

求[1,1]~[x,y],就是p-q。

[tex]p = (x + 1)(y + 1)\sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]} }  + \sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]*i*j} }[/tex]

[tex]q = (x + 1)\sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]*j + (y + 1)\sum\limits_{i = 1}^x {\sum\limits_{j = 1}^y {a[i][j]} } } } *i[/tex]

[tex]

显然我讲的很不详细,所以戳这

(悄悄地告诉你其实窝前面也是抄的论文)

【例题】

BZOJ2789

首先看到一个贪心性质,即第一个串中第k次出现的某个字母对应的一定是第二个中第k次出现的。

那么求个逆序对就阔以了。

BZOJ2743

先求出每个元素下一个与它相同的是第几个。

显然如果[l,r]中有两个同样的元素,该元素的编号为i>=l,则next[i]<=r就阔以了。

那么对询问进行离线操作,其余的还是程序说得更清楚,所以开始看程序吧。

BZOJ1452

对于每种颜色开个二维树状数组就水过了。。。

BZOJ3132

论文题,看完了绝逼会做。

BZOJ3038

显然发现了一个神奇的性质:一个数只要开根了7次后就会变成1,然后就不变了。

那么就用并查集维护变成1的区间不用改了,然后用树状数组维护整个序列。

BZOJ1106

如果两个相同的数间有x个未消掉的数,那么就需要交换x次。

那么只要知道区间内只出现过一次的数的个数就阔以啦!

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=1000010;
int n,i;
long long ans;
char a[M],b[M];
int c[M],tree[M],now[30];
int q[27][M];
int query(int x){
	int ans=0;
	for (int i=x;i>0;i-=i&-i)ans+=tree[i];
	return ans;
}
void update(int x){
	for (int i=x;i<=n;i+=i&-i)tree[i]++;
}
int main(){
	scanf("%d",&n);
	scanf("%s",a+1);
	scanf("%s",b+1);
	for (i=1;i<=n;i++)q[a[i]-'A'][++q[a[i]-'A'][0]]=i;
	for (i=1;i<=n;i++)c[i]=q[b[i]-'A'][++now[b[i]-'A']];
	for (i=n;i>=1;i--){
		ans+=query(c[i]);
		update(c[i]);
	}
	printf("%lld\n",ans);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=1000010;
int n,m,p,i,l;
int a[M],now[M],next[M],ans[M],tree[M];
struct shit{int l,r,id;}q[M];
int cmp(shit a,shit b){return a.l<b.l;}
int query(int x){
	int ans=0;
	for (int i=x;i>0;i-=i&-i)ans+=tree[i];
	return ans;
}
void update(int x,int p){
	for (int i=x;i<=n;i+=i&-i)tree[i]+=p;
}
int main(){
	scanf("%d%d%d",&n,&p,&m);
	for (i=1;i<=n;i++)scanf("%d",&a[i]);
	for (i=n;i>0;i--)next[i]=now[a[i]],now[a[i]]=i;
	for (i=1;i<=p;i++)if (next[now[i]])update(next[now[i]],1);
	for (i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
	sort(q+1,q+m+1,cmp);
	l=1;
	for (i=1;i<=m;i++){
		while (l<q[i].l){
			if (next[l])update(next[l],-1);
			if (next[next[l]])update(next[next[l]],1);
			l++;
		}
		ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
	}
	for (i=1;i<=m;i++)printf("%d\n",ans[i]);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,i,j,opt,q,x,y,ch,x1,x2,y1,y2;
int a[310][310];
int tree[110][310][310];
void update(int opt,int x,int y,int p){
	for (int i=x;i<=n;i+=i&-i)
		for (int j=y;j<=m;j+=j&-j)
			tree[opt][i][j]+=p;
}
int query(int opt,int x,int y){
	int ans=0;
	for (int i=x;i;i-=i&-i)
		for (int j=y;j;j-=j&-j)
			ans+=tree[opt][i][j];
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
		for (j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
			update(a[i][j],i,j,1);
		}
	scanf("%d",&q);
	while (q--){
		scanf("%d",&opt);
		if (opt==1){
			scanf("%d%d%d",&x,&y,&ch);
			update(a[x][y],x,y,-1);
			a[x][y]=ch;
			update(a[x][y],x,y,1);
		}else{
			scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&ch);
			printf("%d\n",query(ch,x2,y2)-query(ch,x1-1,y2)-query(ch,x2,y1-1)+query(ch,x1-1,y1-1));
		}
	}
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
char s[10];
int n,m,x1,x2,y1,y2,ch;
int tree[2100][2100][4];
void update(int x,int y,int p){
	for (int i=x;i<=n;i+=i&-i)
		for (int j=y;j<=m;j+=j&-j){
			tree[i][j][0]+=p;
			tree[i][j][1]+=p*x;
			tree[i][j][2]+=p*y;
			tree[i][j][3]+=p*x*y;
		}
}
int query(int x,int y){
	int ans=0;
	for (int i=x;i;i-=i&-i)
		for (int j=y;j;j-=j&-j)
			ans=ans+tree[i][j][0]*(x+1)*(y+1)-tree[i][j][1]*(y+1)-tree[i][j][2]*(x+1)+tree[i][j][3];
	return ans;
}
int main(){
	scanf("%s",s);
	scanf("%d%d",&n,&m);
	while (scanf("%s",s)!=EOF){
		if (s[0]=='L'){
			scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&ch);
			update(x1,y1,ch);
			update(x2+1,y1,-ch);
			update(x1,y2+1,-ch);
			update(x2+1,y2+1,ch);
		}else{
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			printf("%d\n",query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1));
		}
	}
}
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
typedef long long ll;
using namespace std;
int n,m,i,opt,x,y;
int f[100010];
ll a[100010],tree[100010];
int search(int x){
	if (!f[x])f[x]=x;
	if (f[x]!=x)f[x]=search(f[x]);
	return f[x];
}
ll query(int x){
	ll ans=0;
	for (int i=x;i;i-=i&-i)ans+=tree[i];
	return ans;
}
void update(int x,ll y){
	for (int i=x;i<=n;i+=i&-i)tree[i]+=y;
}
void solve(int x,int y){
	for (int i=search(x);i<=y;i=search(i+1)){
		ll tmp=(ll)sqrt(a[i]);
		update(i,tmp-a[i]);
		a[i]=tmp;
		if (a[i]<=1)f[i]=search(i+1);
	}
}
int main(){
	scanf("%d",&n);
	for (i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		if (a[i]<=1)f[i]=i+1;
		update(i,a[i]);
	}
	scanf("%d",&m);
	for (i=1;i<=m;i++){
		scanf("%d%d%d",&opt,&x,&y);
		if (x>y)swap(x,y);
		if (!opt)solve(x,y);else printf("%lld\n",query(y)-query(x-1));
	}
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,i,ans;
int a[100010],tree[100010],pre[100010];
void update(int x,int y){
	for (int i=x;i<=2*n;i+=i&-i)tree[i]+=y;
}
int query(int x){
	int ans=0;
	for (int i=x;i;i-=i&-i)ans+=tree[i];
	return ans;
}
int main(){
	scanf("%d",&n);
	for (i=1;i<=2*n;i++)scanf("%d",&a[i]);
	for (i=1;i<=2*n;i++){
		if (!pre[a[i]])pre[a[i]]=i,update(i,1);
			else{
				ans+=query(i)-query(pre[a[i]]);
				update(pre[a[i]],-1);
			}
	}
	printf("%d\n",ans);
}
Category: 论蒟蒻的花样作死系列 | Tags:
12
22
2015
0

Contest20151221滚粗记

听说这是JSOIDay7

我去JSOI考不完辣!

值得一提的是:由于这场题目太丧病了,结果最高分80,最低分60。。。

T1

这是一道坑题。(仅仅是因为窝的贪心被卡飞)

首先阔以枚举区间,然后YY各种优化:

1、看区间是不是长度的约数。

2、看区间的第一个和最后一个是不是和整个序列的第一个和最后一个相等。

3、看区间的每一个元素的个数是否合法。

4、看枚举的串有没有出现过。

接着看能不能用枚举的串消掉整个串。

SB贪心:能删则删,那么有反例:aabaabaaaa,即消的顺序会有影响。(然而这有60分)

正确DP:如果区间[i,j]被最大地消去后是模式串的前缀,则称[i,j]为模式串的一个完美匹配。

那么[i,j]为完美匹配的条件为:

1、存在k使得[i,k]与[k+1,j]均为完美匹配,并且k-i+1与j-k至少有一个是模式串长度的倍数,即一段全部消去,一段完美匹配。

2、[i,j-1]为完美匹配,那么就知道了a[j]应该是什么,判断一下就阔以了。

T2

这是一道送分题。(然而分并没有送到)

首先有一个性质:每次中位数与上一次相比只会+1或不变。

那么可以在每次加权后验证中位数是否变化。

那么就可以分块。

既然是第一次写分块,那么就讲一讲:

首先把n个数分成[tex]\sqrt n[/tex]块,每个块中的元素的权值是单调递增的。

对于给一个区间加权,如果一个块的所有元素都在区间内,就打一个tag,否则暴力枚举元素,暴力加,暴力重排。

最后只要二分来看一看大于等于比中位数+1的元素的个数是不是超过一半就阔以辣!

T3

这是一道恶心题。(仅仅是窝不会做)

先挖个坑,待填。

[Codes]

#include<cstdio>
#include<algorithm>
#include<cstring>
typedef unsigned long long ull;
using namespace std;
int cas,n,i,j,len,ans1,ans2;
char s[210];
int a[210],b[210],sum[210][210],f[210][210];
ull mi[210],hash[210];
int isok(int l,int r){
	int i,j,k,p;
	memset(f,0,sizeof f);
	for (i=1;i<=n;i++)if (a[i]==a[l])f[i][i]=1;
	for (p=1;p<n;p++)
		for (i=1;i<=n-p;i++){
			j=i+p;
			for (k=1;k<=p/(len+1);k++)
				if (f[i][i+k*(len+1)]&&f[i+k*(len+1)+1][j]||f[i][j-k*(len+1)]&&f[j-k*(len+1)+1][j]){f[i][j]=1;break;}
			if (f[i][j-1]&&a[j]==a[l+p%(len+1)])f[i][j]=1;
		}
	return f[1][n];
}
int solve(int l1,int l2,int len){
	for (int i=0;i<=len;i++)
		if (a[l1+i]>a[l2+i])return 1;
			else if (a[l1+i]<a[l2+i])return 0;
	return 0;
} 
int main(){
	scanf("%d",&cas);
	mi[0]=1;
	for (i=1;i<=200;i++)mi[i]=mi[i-1]*6662333;
	while (cas--){
		scanf("%s",s);
		n=strlen(s);
		for (i=1;i<=n;i++){
			a[i]=s[i-1]-'A';
			for (j=0;j<=100;j++)sum[i][j]=sum[i-1][j];
			sum[i][a[i]]++;
			hash[i]=hash[i-1]*6662333+a[i];
		}
		int flag=1;
		ull pre=-1;
		for (len=0;len<n&&flag;len++)
			if (!(n%(len+1)))
				for (int l=1;l<=n-len;l++){
					int r=l+len;
					if (a[1]==a[l]&&a[n]==a[r]){
						int cmp=1;
						if (hash[r]-hash[l-1]*mi[len+1]==pre)continue;
						for (i=0;i<=100;i++)if ((sum[r][i]-sum[l-1][i])*n/(len+1)!=sum[n][i]){cmp=0;break;}
						if (cmp&&isok(l,r)){
							if (flag){flag=0;ans1=l;ans2=r;pre=hash[r]-hash[l-1]*mi[len+1];}
								else if (solve(ans1,l,len))ans1=l,ans2=r,pre=hash[r]-hash[l-1]*mi[len+1];
						}
					}
				}
		for (i=ans1;i<=ans2;i++)printf("%c",a[i]+'A');
		puts("");
	}
}
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fi first
#define se second
using namespace std;
const int M=100010;
int n,m,i,j,ans,l,r;
int add[M];
pair<int,int>a[M],b[M];
int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)scanf("%d",&a[i].fi),a[i].se=i,b[i]=a[i];
	sort(b+1,b+n+1);
	ans=b[n+1>>1].fi;
	int x=200,mid=n+1>>1;
	while(n%x)a[++n].fi=0,a[n].se=n;
	for (i=1;i<=n/x;i++)sort(a+(i-1)*x+1,a+i*x+1);
	while (m--){
		scanf("%d%d",&l,&r);
		for (i=1;i<=n/x;i++)
			if (l<=(i-1)*x+1&&r>=i*x)add[i]++;
				else if (r<(i-1)*x+1||l>i*x)continue;
					else{
						for (j=(i-1)*x+1;j<=i*x;j++)
							if (a[j].se>=l&&a[j].se<=r)a[j].fi++;
						sort(a+(i-1)*x+1,a+i*x+1);
					}
		int cnt=0;
		for (i=1;i<=n/x;i++)
			cnt+=i*x-(lower_bound(a+(i-1)*x+1,a+i*x+1,make_pair(ans+1-add[i],0))-a)+1;
		if (cnt>=mid)ans++;
		printf("%d\n",ans);
	}
}
Category: 蒟蒻的滚粗记 | Tags:
12
20
2015
0

BZOJ2738矩阵乘法

【题目简介】

Description

 

  给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

Input

 
  第一行两个数N,Q,表示矩阵大小和询问组数;
  接下来N行N列一共N*N个数,表示这个矩阵;
  再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

Output

  对于每组询问输出第K小的数。

Sample Input

2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3
 

Sample Output

1
3
 

HINT

矩阵中数字是109以内的非负整数;

20%的数据:N<=100,Q<=1000;

40%的数据:N<=300,Q<=10000;

60%的数据:N<=400,Q<=30000;

100%的数据:N<=500,Q<=60000。

Source

【题解】

虽然说好要写su套su,但是打隔膜太high结果时间不够了。。。

第一眼看到题目:果奔二维可持久化线段树?(NiroBC确实这么写了)

既然是离线还是用简单快速的乱搞好辣!

整体二分,对于区间[l,r],询问答案为mid,则将[l,mid]之间的点插入二维树状数组。

然后对于一些询问,如果当前矩阵的元素个数>=k那么就加到左边,否则加到右边。

那么就转化出同样的子问题,左边二分[l,mid],右边二分[mid+1,r]。

于是最后的复杂度就变成了O(mlog3n)辣!

(然而窝的程序狂wa怎么回事)

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=60010;
int n,m,cnt,T,i,j;
int tree[505][505],ans[N],id[N],tmp[N],mark[N];
struct shit{int x1,x2,y1,y2,K;}q[60005];
struct fuck{int x,y,w;}a[250005];
bool operator<(fuck a,fuck b){return a.w<b.w;}
void add(int x,int y,int t){
	for(int i=x;i<=n;i+=i&-i)
		for(int j=y;j<=n;j+=j&-j)
			tree[i][j]+=t;
}
int query(int x,int y){
	int tmp=0;
	for(int i=x;i;i-=i&-i)
		for(int j=y;j;j-=j&-j)
			tmp+=tree[i][j];
	return tmp;
}
int query(int k){
	int x1=q[k].x1,y1=q[k].y1,x2=q[k].x2,y2=q[k].y2;
	return query(x2,y2)+query(x1-1,y1-1)-query(x1-1,y2)-query(x2,y1-1);
}
void solve(int l,int r,int L,int R){
	if(l>r||L==R)return;
	int mid=(L+R)>>1;
	while(a[T+1].w<=mid&&T<cnt){add(a[T+1].x,a[T+1].y,1);T++;}
	while(a[T].w>mid){add(a[T].x,a[T].y,-1);T--;}
	int cnt=0;
	for(int i=l;i<=r;i++)
		if(query(id[i])>q[id[i]].K-1){
			mark[i]=1;ans[id[i]]=mid;cnt++;
		}else mark[i]=0;
	int l1=l,l2=l+cnt;
	for(int i=l;i<=r;i++)
		if(mark[i])tmp[l1++]=id[i];
			else tmp[l2++]=id[i];
	for(int i=l;i<=r;i++)id[i]=tmp[i];
	solve(l,l1-1,L,mid);
	solve(l1,l2-1,mid+1,R);
}
int main(){
	scanf("%d%d",&n,&m);
	int mx=0;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++){
			a[++cnt].x=i,a[cnt].y=j,scanf("%d",&a[cnt].w);
			mx=max(a[cnt].w,mx);
		}
	sort(a+1,a+cnt+1);
	for(i=1;i<=m;i++)scanf("%d%d%d%d%d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2,&q[i].K);
	for(i=1;i<=m;i++)id[i]=i;
	solve(1,m,0,mx+1);
	for(i=1;i<=m;i++)printf("%d\n",ans[i]);
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
20
2015
0

BZOJ3505【Cqoi2014】数三角形

3505: [Cqoi2014]数三角形

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 851  Solved: 525
[Submit][Status][Discuss]

Description

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output


输出一个正整数,为所求三角形数量。

Sample Input


2 2

Sample Output

76

 
数据范围
1<=m,n<=1000

HINT

 

Source

【题解】

等下要刷一道su套su,先刷道水题压压gin。

首先在n*m个点中任取三个。

接着减去三点共线的:同行、同列、斜率不为0的。

对于斜线,枚举两点的坐标差,那么易求这样的点对的个数、两点之间的整点数。

那么就阔以辣!

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
ll n,m,i,j;
ll gcd(int x,int y){
	if (!(x%y))return y;else return gcd(y,x%y);
}
int main(){
	scanf("%lld%lld",&n,&m);
	n++;m++;
	ll ans=(n*m-2)*(n*m-1)*n*m/6;
	ans-=n*(m-2)*(m-1)*m/6;
	ans-=m*(n-2)*(n-1)*n/6;
	for (i=1;i<n;i++)
		for (j=1;j<m;j++)
			ans-=2*(n-i)*(m-j)*(gcd(i,j)-1);
	printf("%lld\n",ans);
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
20
2015
0

Contest20151217滚粗记

听说这是JSOIDay6(为什么江苏有这么多省选)

膜了贵省的题目,感到不明觉厉。

T1

这是一道人类智慧题。(其实是窝想错了)

考试时不断YY费用流的模型。(其实是被Gold_7误导了)

然而这是一道区间dp。

把每个敌人抽象为左端点为ai,右端点为bi,高度为ci的线段。

对于区间[l,r],找到其中最高的线段,枚举上面的点,然后用一条直线切断它以及它下方可以切断的点,那么就阔以转移辣!

T2

这是一道难题。(其实是因为gonens不会做)

那么就介绍一下60分蒜法:枚举根,dfs,然后在线段树上修改和询问就阔以辣!

(然而窝忘记开longlong了)

T3

这是一道码农题。(其实是窝写得臭)

考试的时候发现在线是用来搞笑的。

后来正解说明在线确实是用来搞笑的。

首先从大到小枚举边,并查集维护连通性,如果不在同一个集合里就直接加上去,否则找到环上最大的边,删去。

接着用可持久化线段树维护一下选择的边。

最后对于区间[l,r]就是在编号为l的线段树上询问[l,r]。

(然而其中的离散实在是太蛋疼了)

[Codes]

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=1010;
int cas,n,i,j,k,p,cnt;
int a[M],b[M],c[M],g[M],A[M],f[M][M];
int main(){
		memset(f,0,sizeof f);
		scanf("%d",&n);
		cnt=0;
		for (i=1;i<=n;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]),A[++cnt]=a[i],A[++cnt]=b[i];
		sort(A+1,A+cnt+1);
		cnt=unique(A+1,A+cnt+1)-A-1;
		for (i=1;i<=n;i++)a[i]=lower_bound(A+1,A+cnt+1,a[i])-A,b[i]=lower_bound(A+1,A+cnt+1,b[i])-A;
		for (p=1;p<cnt;p++)
			for (i=1;i<=cnt-p;i++){
				memset(g,0,sizeof g);
				int j=i+p,mx=0;
				f[i][j]=1000000007;
				for (k=1;k<=n;k++)
					if (a[k]>=i&&b[k]<=j&&c[k]>mx)mx=c[k];
				if (!mx){f[i][j]=0;continue;}
				for (k=1;k<=n;k++)
					if (a[k]>=i&&b[k]<=j&&c[k]==mx)g[b[k]+1]--,g[a[k]]++;
				for (k=i+1;k<=j;k++)g[k]+=g[k-1];
				for (k=i;k<=j;k++)
					if (g[k]>0)f[i][j]=min(f[i][j],f[i][k-1]+f[k+1][j]+mx);
			}
		printf("%d\n",f[1][cnt]);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1000010;
const int M=5010;
long long ans1;
int n,i,x,y,ch,edgenum,ans2;
int head[M],vet[M],next[M],len[M],flag[M];
int tree[maxn*4];
void addedge(int x,int y,int ch){
	vet[++edgenum]=y;
	next[edgenum]=head[x];
	head[x]=edgenum;
	len[edgenum]=ch;
}
int query(int x,int l,int r,int p){
	if (l==r)return l;
	int mid=l+r>>1;
	if (tree[p<<1]>=x)return query(x,l,mid,p<<1);
		else return query(x-tree[p<<1],mid+1,r,p<<1|1);
}
void update(int x,int l,int r,int p,int t){
	tree[p]+=t;
	if (l==r)return;
	int mid=l+r>>1;
	if (x<=mid)update(x,l,mid,p<<1,t);
		else update(x,mid+1,r,p<<1|1,t);
}
void dfs(int u,int dep){
	int e,v;
	flag[u]=1;
	if (dep&1)ans1+=query(dep/2+1,1,maxn,1),ans2++;
	for (e=head[u];e;e=next[e]){
		v=vet[e];
		if (flag[v])continue;
		update(len[e],1,maxn,1,1);
		dfs(v,dep+1);
		update(len[e],1,maxn,1,-1);
	}
}
int main(){
	scanf("%d",&n);
	for (i=1;i<n;i++){
		scanf("%d%d%d",&x,&y,&ch);
		addedge(x,y,ch);
		addedge(y,x,ch);
	}
	for (i=1;i<=n;i++){
		memset(flag,0,sizeof flag);
		dfs(i,0);
	}
	printf("%.7lf\n",(double)ans1/ans2);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=200010;
int n,m,i,j,sum,q,opt,x,y,ans,edgenum,cnt,mx,tot;
int A[M],B[M],b[M],root[M],f[M],id[M],fi[M];
int head[1010],edg[1010],vet[M],next[M],len[M],isok[M],data[M];
struct shit{int x,y,w;}a[M];
struct node{int l,r,w;}tree[M*40];
bool cmp(shit x,shit y){return x.w>y.w;}
void addedge(int x,int y,int ch,int w){
	vet[++edgenum]=y;
	next[edgenum]=head[x];
	head[x]=edgenum;
	len[edgenum]=ch;
	data[edgenum]=w;
}
int dfs(int u,int pre,int st){
	int e,v;
	if (u==st&&pre!=-1)return 1;
	for (e=head[u];e;e=next[e]){
			v=vet[e];
			if (e==(pre^1))continue;
			if (dfs(v,e,st)){
				if (len[e]>len[mx])mx=e;
				return 1;
			}
		}
	return 0;
}
int search(int x){
	if (f[x]!=x)f[x]=search(f[x]);
	return f[x];
}
void update(int x,int l,int r,int &p,int t){
	tree[++cnt]=tree[p],p=cnt,tree[p].w+=t;
	if (l==r)return;
	int mid=l+r>>1;
	if (x<=mid)update(x,l,mid,tree[p].l,t);
		else update(x,mid+1,r,tree[p].r,t);
}
int query(int x,int y,int l,int r,int p){
	if (x>y)return 0;
	if (x==l&&y==r)return tree[p].w;
	int mid=l+r>>1;
	if (y<=mid)return query(x,y,l,mid,tree[p].l);
		else if (x>mid)return query(x,y,mid+1,r,tree[p].r);
			else return query(x,mid,l,mid,tree[p].l)+query(mid+1,y,mid+1,r,tree[p].r);
}
int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
	int mm=0;
	for (i=1;i<=m;i++)if (a[i].x!=a[i].y)a[++mm]=a[i];
	m=mm;
	sort(a+1,a+m+1,cmp);
	for (i=1;i<=m;i++)A[i]=a[m-i+1].w;
	sum=unique(A+1,A+m+1)-A-1;
	for (i=1;i<=m;i++)id[i]=lower_bound(A+1,A+sum+1,a[i].w)-A;
	for (i=1;i<=m;i++)id[i]=sum-id[i]+1;
	for (i=1;i<=n;i++)f[i]=i;
	for (i=1;i<=m;i++){
		edgenum=1;
		memset(head,0,sizeof head);
		for (j=1;j<=edg[0];j++)addedge(a[edg[j]].x,a[edg[j]].y,a[edg[j]].w,edg[j]),addedge(a[edg[j]].y,a[edg[j]].x,a[edg[j]].w,edg[j]);
		root[i]=root[i-1];
		fi[id[i]]=i;
		edg[++edg[0]]=i;
		addedge(a[i].x,a[i].y,a[i].w,i);
		addedge(a[i].y,a[i].x,a[i].w,i);
		update(id[i],1,sum,root[i],a[i].w);
		int p=search(a[i].x),q=search(a[i].y);
		if (p!=q){
			f[p]=q;
			continue;
		}
		mx=0;
		dfs(a[i].x,-1,a[i].x);
		mx=data[mx];
		update(id[mx],1,sum,root[i],-a[mx].w);
		int mm=0;
		for (j=1;j<=edg[0];j++)if (edg[j]!=mx)edg[++mm]=edg[j];
		edg[0]=mm;
	}
	scanf("%d%d",&q,&opt);
	while (q--){
		scanf("%d%d",&x,&y);
		int l=ans*opt+x,r=l+y,pre=r;
		if (l>A[sum]){puts("0");ans=0;continue;}
		l=lower_bound(A+1,A+sum+1,l)-A;
		r=upper_bound(A+1,A+sum+1,r)-A-1;
		r=sum-r+1,l=sum-l+1;
		ans=query(r,l,1,sum,root[fi[l]]);
		printf("%d\n",ans);
	}
}
Category: 蒟蒻的滚粗记 | Tags:
12
16
2015
0

BZOJ1251序列终结者

【题目简介】

Description

网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道题目 就叫序列终结者吧。 【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。

Input

第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。

Output

对于每个第3种操作,给出正确的回答。

Sample Input

4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4
 

Sample Output

2
【数据范围】
N<=50000,M<=100000。
 

HINT

 

Source

【题解】

对此窝还能说什么呢。。。

由于之前的模板写得过于臭长,于是参考(抄写)了Hzwer的Splay模板。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=100010;
int n,m,i,cnt,root,l,r,x,opt;
int fa[M],son[M][2],tag[M],rev[M],data[M],size[M],mx[M];
void pushup(int p){
	int l=son[p][0],r=son[p][1];
	mx[p]=max(max(mx[l],mx[r]),data[p]);
	size[p]=size[l]+size[r]+1;
}
void pushdown(int p){
	int l=son[p][0],r=son[p][1],t=tag[p];
	if (t){
		tag[p]=0;
		if (l){tag[l]+=t;mx[l]+=t;data[l]+=t;}
		if (r){tag[r]+=t;mx[r]+=t;data[r]+=t;}
	}
	if (rev[p]){
		rev[p]=0;
		rev[l]^=1;rev[r]^=1;
		swap(son[p][0],son[p][1]);
	}
}
void build(int l,int r,int p){
	if(l>r)return;
	if (l==r){
		fa[l]=p;size[l]=1;
		if (l<p)son[p][0]=l;else son[p][1]=l;
		return;
	}
	int mid=l+r>>1;
	build(l,mid-1,mid);
	build(mid+1,r,mid);
	fa[mid]=p;pushup(mid);
	if (mid<p)son[p][0]=mid;else son[p][1]=mid;
}
void rotate(int x,int &k){
	int y=fa[x],z=fa[y],l,r;
	if (son[y][0]==x)l=0;else l=1;r=l^1;
	if (y==k)k=x;
		else {if (son[z][0]==y)son[z][0]=x;else son[z][1]=x;}
	fa[x]=z;fa[y]=x;fa[son[x][r]]=y;
	son[y][l]=son[x][r];son[x][r]=y;
	pushup(y);pushup(x);
}
void splay(int x,int &k){
	while (x!=k){
		int y=fa[x],z=fa[y];
		if (y!=k){
			if (son[y][0]==x^son[z][0]==y)rotate(x,k);
				else rotate(y,k);
		}
		rotate(x,k);
	}
}
int findkth(int k,int rank){
	if (tag[k]||rev[k])pushdown(k);
	int l=son[k][0],r=son[k][1];
	if (size[l]+1==rank)return k;
		else if (size[l]>=rank)return findkth(l,rank);
			else return findkth(r,rank-size[l]-1);
}
void update(int l,int r,int val){
	int x=findkth(root,l),y=findkth(root,r+2);
	splay(x,root);splay(y,son[x][1]);
	int z=son[y][0];
	tag[z]+=val;data[z]+=val;mx[z]+=val;
}
void reverse(int l,int r){
	int x=findkth(root,l),y=findkth(root,r+2);
	splay(x,root);splay(y,son[x][1]);
	int z=son[y][0];
	rev[z]^=1;
}
void query(int l,int r){
	int x=findkth(root,l),y=findkth(root,r+2);
	splay(x,root);splay(y,son[x][1]);
	int z=son[y][0];
	printf("%d\n",mx[z]);
}
int main(){
	mx[0]=-1000000007;
	scanf("%d%d",&n,&m);
	build(1,n+2,0);root=(n+3)>>1;cnt=n+2;
	for (i=1;i<=m;i++){
		scanf("%d",&opt);
		switch(opt){
			case 1:scanf("%d%d%d",&l,&r,&x);update(l,r,x);break;
			case 2:scanf("%d%d",&l,&r);reverse(l,r);break;
			case 3:scanf("%d%d",&l,&r);query(l,r);break;
		}
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
15
2015
0

Contest20151214滚粗记

听说这是JSOIDay5

膜了贵省的题目,感到不明觉厉。

条哥出去吔东西了,于是没有断网。

为了比赛公平,窝决定先泼会隔膜。

T1

这是一道好题。(然而窝不会做)

NiroBC将此题吊着打太强辣!!!

考虑三维容斥,即选i行j列k中颜色,表示i行j列可染可不染,另外n-i行和m-j列必定不染,k种颜色可用可不用,c-k种颜色必定不用。

那么答案就是

[tex]\sum\limits_{i = 0}^n {\sum\limits_{j = 0}^m {\sum\limits_{k = 0}^c {\mathop C\nolimits_n^i \mathop C\nolimits_m^j } } } \mathop C\nolimits_c^k {(k + 1)^{ij}}{( - 1)^{n + m + c - i - j - k}}[/tex]

于是n3枚举i、j、k就可以辣!(NiroBC还有一个不明觉厉的n2logn的做法)

T2

这是一道送分题。(其实是窝做过)

首先考虑这样一种暴力:枚举i,然后在前面找j,求出[j,i]的gcd。

那么可以优化,即已经知道了gcd[i,j],那么gcd[i,j+1]=gcd(a[j+1],gcd[i,j])。

显然会发现有许多gcd是相同的,那么对于后面就没用辣~

那么只要维护一下不同的gcd就能AC了。(虽然理论上是n2的但是卡它的数据太难出了Orz)

T3

这是一道恶心题。(其实是窝懒得写)

首先对于每一条地铁线路构一条新的路径,其中上站的边权为1,0,下站的边权为0,0,中途乘坐的边权为0,1。

那么只要让第一维边权最短的情况下第二维边权最长,跑最短路就可以辣!

然并卵Spfa被卡飞。。

于是可以先dijkstra处理第一维最短路,那么出现在最短路上的边一定构成了一个DAG。

那么就可以拓扑排序辣!

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
const int mod=1000000007;
int n,m,p,i,j,k;
ll ans;
ll c[410][410];
ll mi[410][160010];
int main(){
	scanf("%d%d%d",&n,&m,&p);
	c[0][0]=1;
	for (i=1;i<=max(n,max(m,p));i++){
		c[i][0]=1;
		for (j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
	}
	for (i=1;i<=p+1;i++){
		mi[i][0]=1;
		for (j=1;j<=n*m;j++)mi[i][j]=mi[i][j-1]*i%mod;
	}
	for (i=0;i<=n;i++)
		for (j=0;j<=m;j++)
			for (k=0;k<=p;k++){
				ll tmp=c[n][i]*c[m][j]%mod*c[p][k]%mod*mi[k+1][i*j]%mod;
				if ((n+m+p-i-j-k)&1)ans=(ans-tmp+mod)%mod;else ans=(ans+tmp)%mod;
			}
	printf("%lld\n",ans);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
const int M=200010;
int n,i,j,r;
ll ans;
ll a[M],h[M][2],q[M][2];
ll gcd(ll x,ll y){
	if (!(x%y))return y;else return gcd(y,x%y);
}
int main(){
	scanf("%d",&n);
	for (i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		for (j=1;j<=r;j++)h[j][0]=gcd(h[j][0],a[i]);
		h[++r][0]=a[i],h[r][1]=i;
		int cnt=1;
		q[1][0]=h[1][0],q[1][1]=h[1][1];
		ans=max(ans,h[1][0]*(i-h[1][1]+1));
		for (j=2;j<=r;j++)
			if (h[j][0]!=h[j-1][0]){
				q[++cnt][0]=h[j][0],q[cnt][1]=h[j][1];
				ans=max(ans,h[j][0]*(i-h[j][1]+1));
			}
		r=cnt;
		for (j=1;j<=cnt;j++)h[j][0]=q[j][0],h[j][1]=q[j][1];
	}
	printf("%lld\n",ans);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#define fi first
#define se second
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int> PII;
const int M=5000010;
const int inf=1000000007;
int n,m,i,j,y,st,ed,tmp,cnt,edgenum,Edgenum;
char s[50];
int head[M],vet[M],next[M],Head[M],Vet[M],Next[M],Len[M],init[M],dis[M],Dis[M],len[M][2],inq[M],x[M],q[M];
priority_queue<PII,vector<PII>,greater<PII> >Q;
pair<ull,int>a[M];
int solve(){
	ull tmp=0;
	for (int i=0;i<strlen(s);i++)tmp=tmp*2333+s[i];
	int ans=a[lower_bound(a+1,a+m+1,make_pair(tmp,0))-a].se;
	return ans;
}
void addedge(int x,int y,int p,int q){
	vet[++edgenum]=y;
	next[edgenum]=head[x];
	head[x]=edgenum;
	len[edgenum][0]=p;
	len[edgenum][1]=q;
}
void Addedge(int x,int y,int p){
	Vet[++Edgenum]=y;
	Next[Edgenum]=Head[x];
	Head[x]=Edgenum;
	Len[Edgenum]=p;
	init[y]++;
}
void dijkstra(){
	for (i=1;i<=cnt;i++)dis[i]=inf;
	Q.push(make_pair(0,st));dis[st]=0;
	for (i=1;i<=cnt;i++)inq[i]=0;
	while (!Q.empty()){
		int u=Q.top().se;Q.pop();
		if (inq[u])continue;
		inq[u]=1;
		for (int e=head[u];e;e=next[e]){
			int v=vet[e];
			if (dis[u]+len[e][0]<dis[v]){
				dis[v]=dis[u]+len[e][0];
				Q.push(make_pair(dis[v],v));
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++){
		scanf("%s",s);
		for (j=0;j<strlen(s);j++)a[i].fi=a[i].fi*2333+s[j];
		a[i].se=i;
	}
	sort(a+1,a+m+1);
	cnt=m;
	for (i=1;i<=n;i++){
		scanf("%d",&tmp);
		scanf("%s",s);
		x[1]=solve();
		addedge(x[1],++cnt,1,0);
		for (j=2;j<=tmp;j++){
			scanf("%s",s);
			y=cnt,x[j]=solve();
			addedge(y,++cnt,0,1);
			addedge(x[j],cnt,1,0);
			addedge(cnt,x[j],0,0);
		}
		addedge(x[tmp],++cnt,1,0);
		for (j=tmp-1;j>=1;j--){
			y=cnt;
			addedge(y,++cnt,0,1);
			addedge(x[j],cnt,1,0);
			addedge(cnt,x[j],0,0);
		}
	}
	scanf("%s",s);
	st=solve();
	scanf("%s",s);
	ed=solve();
	dijkstra();
	for (i=1;i<=cnt;i++)
		for (int e=head[i];e;e=next[e])
			if (dis[i]+len[e][0]==dis[vet[e]])Addedge(i,vet[e],len[e][1]);
	int l=1,r=0;
	for (i=1;i<=cnt;i++)if (!init[i])q[++r]=i;
	while (l<=r){
		int u=q[l++];
		for (int e=Head[u];e;e=Next[e]){
			int v=Vet[e];
			init[v]--;
			if (init[v]==0)q[++r]=v;
		}
	}
	for (i=1;i<=cnt;i++){
		int u=q[i];
		for (int e=Head[u];e;e=Next[e]){
			int v=Vet[e];
			Dis[v]=max(Dis[v],Dis[u]+Len[e]);
		}
	}
	printf("%d\n%d\n",dis[ed],Dis[ed]);
}
Category: 蒟蒻的滚粗记 | Tags:
12
13
2015
0

Contest20151211滚粗记

听说这是JSOIDay4

膜了贵省的题目,感到不明觉厉。

T1

咦这是什么题目?

5min过去了。。。听说NiroBC已经AK弃疗了。。。

Orz好像70分是送的。。

那么想一想正解吧。

考虑一条u->v的边,那么这条边被删掉的条件是:存在另一条路径使得u能到v。

那么分块,如果u的出度小于[tex]\sqrt n[/tex],就枚举u的出边,否则枚举v的入边。

那么问题就转化为判断两点的连通性。

那么只要拓扑+压位就可以辣!

T2

咦这是什么题目?

好吧先yy个20分暴力。

接着Gold_7将此题AC,感到不明觉厉。

正解是:

将正权连源,负权连汇,按照墙的权值建图,跑最小割。

ans=权值绝对值之和-最小割。

正确性在于:假如存在一条源到汇的路径,那么显然:

1、不取正权的点。

2、不取负权的点。

3、用墙将它们隔开。

那么只要把这条路径隔断就可以了。

T3

咦这是什么题目?

好吧先yy个20分暴力。

结果写萎了。。

蛋疼的满分算法如下:

显然最大值的长度len为[tex]\left[ {\frac{{n - 1}}{k}} \right] + 1[/tex]

那么可以对所有点向后扩展len位,排序,显然答案必定是这些数其中一个。

那么二分,每个点能扩展len位就扩展,否则扩展len-1位。

接着只要判断一下有没有一种扩展方式在扩展了k次的限制下超过了n位就可以辣!

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
typedef long long ll;
using namespace std;
const int M=200010;
const int N=30010;
int n,m,i,l,r,edgenum,edgenum1,ans;
int head[M],vet[M],next[M],price[M],head1[M],vet1[M],next1[M],x[M],y[M],h[M];
ll f[N][1010];
void addedge(int x,int y){
	vet[++edgenum]=y;
	next[edgenum]=head[x];
	head[x]=edgenum;
	price[y]++;
}
void addedge1(int x,int y){
	vet1[++edgenum1]=y;
	next1[edgenum1]=head1[x];
	head1[x]=edgenum1;
	price[y]++;
}
int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++)scanf("%d%d",&x[i],&y[i]),addedge(y[i],x[i]);
	for (i=1;i<=n;i++){
		if (!price[i])h[++r]=i;
		f[i][i/30]=(1<<(i%30));
	}
	l=1;
	while (l<=r){
		int u=h[l];
		for (int e=head[u];e;e=next[e]){
			int v=vet[e];
			for (i=0;i<=n/30;i++)f[v][i]|=f[u][i];
			price[v]--;
			if (!price[v])h[++r]=v;
		}
		l++;
	}
	for (i=1;i<=m;i++)addedge1(x[i],y[i]);
	for (i=1;i<=m;i++)
		if (price[x[i]]<=sqrt(n)){
			for (int e=head1[x[i]];e;e=next1[e]){
				int v=vet1[e];
				if (v==y[i])continue;
				if (f[v][y[i]/30]&(1<<(y[i]%30))){ans++;break;}
			}
		}else{
			for (int e=head[y[i]];e;e=next[e]){
				int v=vet[e];
				if (v==x[i])continue;
				if (f[x[i]][v/30]&(1<<(v%30))){ans++;break;}
			}
		}
	printf("%d\n",ans);
}
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=400010;
int n,m,i,j,edgenum,source,sink,cnt,ans,x; 
int head[M],vet[M],next[M],size[M],h[M],q[M];
int point[210][210];
void addedge(int x,int y,int ch){
	vet[++edgenum]=y;
	next[edgenum]=head[x];
	head[x]=edgenum;
	size[edgenum]=ch;
	vet[++edgenum]=x;
	next[edgenum]=head[y];
	head[y]=edgenum;
}
int bfs(){
	for (int i=1;i<=M;i++)h[i]=-1;
	int l=1,r=1;
	q[1]=source,h[source]=0;
	while (l<=r){
		int u=q[l];
		for (int e=head[u];e;e=next[e])
			if (size[e]>0&&h[vet[e]]<0){
				int v=vet[e];
				h[v]=h[u]+1;
				q[++r]=v;
			}
		l++;
	}
	if (h[sink]<0)return 0;else return 1;
}
int dfs(int u,int aug){
	if (u==sink)return aug;
	int flow=0;
	for (int e=head[u];e;e=next[e])
		if (size[e]>0&&h[vet[e]]==h[u]+1){
			int v=vet[e];
			int tmp=dfs(v,min(aug-flow,size[e]));
			size[e]-=tmp;
			size[e^1]+=tmp;
			flow+=tmp;
			if (flow==aug)return flow;
		}
	if (!flow)h[u]=-1;
	return flow;
}
int main(){
	scanf("%d%d",&n,&m);
	source=1,sink=n*m+2,cnt=1,edgenum=1;
	for (i=1;i<=n;i++)
		for (j=1;j<=m;j++){
			scanf("%d",&x);
			point[i][j]=++cnt;
			if (x>0)addedge(source,point[i][j],x),ans+=x;else if (x<0)addedge(point[i][j],sink,-x),ans-=x;
		}
	for (i=1;i<n;i++)
		for (j=1;j<=m;j++){
			scanf("%d",&x);
			addedge(point[i][j],point[i+1][j],x);
			addedge(point[i+1][j],point[i][j],x);
		}
	for (i=1;i<=n;i++)
		for (j=1;j<m;j++){
			scanf("%d",&x);
			addedge(point[i][j],point[i][j+1],x);
			addedge(point[i][j+1],point[i][j],x);
		}
	while (bfs())ans-=dfs(source,1000000007);
	printf("%d\n",ans);
}
Category: 蒟蒻的滚粗记 | Tags:

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