【Codeforces 626F】Group Projects

相关链接

题目传送门:http://codeforces.com/problemset/problem/626/F
中文题面:http://h0rnet.hatenablog.com/entry/2016/11/12/CFR_626_F__Group_Projects_(DP)

解题报告

考虑把每个人放到值域上
于是问题转化为搞一些线段,使得总长度之和小于K

考虑每一个节点的决策:
1. 成为线段左端点
2. 成为线段右端点
3. 放到某个线段中间/自己一组

于是我们就有了一个$DP$:
$f[i][j][k]$表示已经处理完前i个数,还有j各线段左端点未配对,线段总长为k的方案数
转移如上文所述,是$O(3)$的
于是复杂度为$O(k*n^2)$

Code

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int MOD = 1000000007;

int f[2][200+9][1000+9],n,arr[1000+9],w,p,k,cnt[500+9],tot,que[1000+9];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

int main() {
	n = read(); k = read(); w = 1, p = 0;
	for (int i=1;i<=n;i++) cnt[arr[i]=read()]++;
	for (int i=1;i<=500;i++) {
		if (cnt[i]) {
			que[++tot] = i; 
			cnt[tot] = cnt[i];
		}
	}
	que[0] = que[1];
	
	f[p][0][k] = 1;
	for (int i=1,delta;i<=tot;i++) {
		memset(f[w],0,sizeof(f[w]));
		delta = que[i] - que[i-1];
		for (int j=0;j<=n;j++) {
			for (int t=j*delta;t<=k;t++) {
				f[w][j][t-j*delta] += f[p][j][t];
			}
		}
		swap(w, p);
		
		for (int x=1;x<=cnt[i];x++,w^=1,p^=1) {
			memset(f[w],0,sizeof(f[w]));	
			for (int j=0;j<=n;j++) {
				for (int t=0;t<=k;t++) {
					if (f[p][j][t]) {
						LL tmp = f[p][j][t];
						(f[w][j][t] += tmp * (j + 1) % MOD) %= MOD;
						if (j) (f[w][j-1][t] += tmp * j % MOD) %= MOD;
						(f[w][j+1][t] += tmp) %= MOD;				
					}
				}
			}
		}
	}
	int ret = 0;
	for (int i=0;i<=k;i++) 
		ret = (ret + f[p][0][i]) % MOD;
	printf("%d\n",ret);
	return 0;
}

【UVa 1027】Toll

题目传送门:https://uva.onlinejudge.org/index.php?problem=3468
中文题面:《算法竞赛·入门经典·训练指南》P331

一直以为是一个建图非常神奇的最短路/网络流
然而居然是类似斯坦纳树一样的转移怪异的DP
qrrxcieey4qta4clrvl

设d[i]为满足题目条件的情况下,到达第i个点时的最少货物量
然后反向各更新即可

更新的方式的话,显然SPFA的正确性没有问题
又因为没有负权,所以Dijkstra也是正确的?

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 70;
const int M = 2000+9;
const double EPS = 1e-4;

int head[N],nxt[M],to[M],m,T,MN,s,t,case_cnt;
LL d[N]; bool inq[N]; queue<int> que; 

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline int ID(const char c) {
	if ('a' <= c && c <= 'z') return c - 'a' + 1;
	else return c - 'A' + 27;
}

inline void Add_Edge(int u, int v) {
	to[++T] = v; nxt[T] = head[u]; head[u] = T;
	to[++T] = u; nxt[T] = head[v]; head[v] = T;
}

inline LL cost(int w, LL val) {
	if (w == s) return 0;
	else if (w <= 26) return 1;
	else {
		LL l=1,r=1e15,ret=1,mid;
		while (l <= r) {
			mid = l + r >> 1;
			if (val+mid-(LL)(ceil((val+mid)/20.0)+EPS) >= val) ret = mid, r = mid - 1;
			else l = mid+1;
		}
		return ret;
	}
}	

inline LL SPFA() {
	memset(d,0x3f,sizeof(d));
	d[t] = MN + (s!=t?cost(t, MN):0); 
	inq[t] = 1; que.push(t);
	
	while (!que.empty()) {
		int w = que.front(); inq[w] = 0; que.pop();
		for (int i=head[w];i;i=nxt[i]) {
			if (d[to[i]] > d[w] + cost(to[i],d[w])) {
				d[to[i]] = d[w] + cost(to[i],d[w]);
				if (!inq[to[i]]) inq[to[i]] = 1, que.push(to[i]);
			}
		}
	}
	return d[s];
}

int main(){
	static char U[10],V[10];
	for (m=read();~m;m=read(),T=0) {
		memset(head,0,sizeof(head));
		
		for (int i=1;i<=m;i++) {
			scanf("%s %s",U+1,V+1);
			Add_Edge(ID(U[1]), ID(V[1]));
		}
		
		MN = read();
		scanf("%s %s",U+1,V+1);
		s = ID(U[1]); t = ID(V[1]);
		printf("Case %d: %lld\n",++case_cnt,SPFA());
	}
	return 0;
}

【UVa 10635】Prince and Princess

题目传送门:https://uva.onlinejudge.org/index.php&problem=1576
中文题面:《算法竞赛·入门经典·训练指南》P66

这题真的是好妙啊!
wv6v0iqq6vgq9xxczbs2c

首先O(n^4)的DP大家都会吧?
来说一说O(n*log(n))的做法:
将A串重新编号为1、2、3、4、5····
然后将编码的对应关系应用到B上面去
这样的话,就变成了求B的LIS了
于是搞一个BIT什么的就可以辣!

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 250+9;
const int M = N * N;

int n,m,q,p,A[M],B[M],trans[M],vout[M];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

namespace Fenwick_Tree{
	#define BIT Fenwick_Tree
	#define lowbit(x) ((x)&-(x))
	int MX[M];
	
	inline void modify(int pos, int val) {
		for (int i=pos;i<=m;i+=lowbit(i)) 
			MX[i] = max(MX[i], val);
	}
	
	inline int query(int pos) {
		int ret = 0;
		for (int i=pos;i;i-=lowbit(i)) 
			ret = max(ret, MX[i]);
		return ret;
	}
};

int main(){
	for (int k=1,T=read();k<=T;k++) {
		n = read(); m = n * n + 1;
		p = read() + 1; q = read() + 1;
		for (int i=1;i<=p;i++) A[i] = read();
		for (int i=1;i<=q;i++) B[i] = read();
		
		memset(trans,0,sizeof(trans));
		memset(vout,0,sizeof(vout));
		memset(BIT::MX,0,sizeof(BIT::MX));
		for (int i=1;i<=p;i++) trans[A[i]] = i;
		for (int i=1;i<=q;i++) B[i] = trans[B[i]];
		for (int i=1;i<=q;i++) if (B[i]) {
			vout[i] = BIT::query(B[i]) + 1;
			BIT::modify(B[i],vout[i]);
		}
		
		int ret = 0;
		for (int i=1;i<=q;i++) 
			ret = max(ret, vout[i]);
		printf("Case %d: %d\n",k,ret);
	} 
	return 0;
}

【BZOJ 2286】[Sdoi2011] 消耗战

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2286
数据生成器:http://paste.ubuntu.com/23372796/

就是把虚树建出来了以后,在虚树上跑树上DP
DP很好想,只需要会建虚树就可以辣!

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int SGZ = 18;
const int M = 500000+9;
const int N = 250000+9;
const int INF = 0x7fffffff;

int head[N],nxt[M],to[M],cost[M],dep[N],fa[N][SGZ],MN[N][SGZ];
int n,m,p[N],num[N],stk[N],top,tot,T;
bool vis[N]; 

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Add_Edge(int u, int v) {
	to[++T] = v; nxt[T] = head[u]; head[u] = T;
}

inline void Add_Edge(int u, int v, int w) {
	to[++T] = v; nxt[T] = head[u]; head[u] = T; cost[T] = w;
	to[++T] = u; nxt[T] = head[v]; head[v] = T; cost[T] = w;
}

void DFS(int w, int f) {
	static int dfs_cnt = 0; num[w] = ++dfs_cnt;
	fa[w][0] = f; dep[w] = dep[f] + 1;
	for (int i=head[w];i;i=nxt[i]) {
		if (to[i] != f) {
			DFS(to[i], w);
			MN[to[i]][0] = cost[i];
		}
	}
}

inline bool CMP(const int &x, const int &y) {
	return num[x] < num[y];
}

inline int LCA(int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	for (int i=17;~i;i--) {
		if (dep[fa[x][i]] >= dep[y]) {
			x = fa[x][i];
		}
	}
	if (x == y) return x;
	for (int i=17;~i;i--) {
		if (fa[x][i] != fa[y][i]) {
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	return fa[x][0];
} 

inline int Get_Cost(int w, int pur) {
	int ret = INF; pur = dep[pur];
	for (int i=17;~i;i--) {
		if (dep[fa[w][i]] >= pur) {
			ret = min(ret, MN[w][i]);
			w = fa[w][i];
		}
	}
	return ret;
}

inline void build() {
	stk[top = 1] = 1;
	for (int i=1,w,lca;i<=m;i++) {
		w = p[i]; lca = LCA(stk[top], w);
		while (dep[stk[top]] > dep[lca]) {
			if (dep[stk[top-1]] <= dep[lca]) 
				Add_Edge(lca, stk[top]);
			else 
				Add_Edge(stk[top-1], stk[top]);
			--top;
		}
		if (stk[top] != lca) stk[++top] = lca, p[++tot] = lca;
		stk[++top] = w;
	}
	for (int i=1;i<top;i++) 
		Add_Edge(stk[i], stk[i+1]);
}

inline LL DP(int w, int f) {
	LL tmp = 0;
	for (int i=head[w];i;i=nxt[i]) 
		tmp += DP(to[i], w);
	if (vis[w]) return Get_Cost(w, f);
	else if (w != 1) return min((LL)Get_Cost(w, f), tmp);
	else return tmp;
}

int main() {
	n = read();
	for (int i=1,u,v;i<n;i++) {
		u = read(); v = read();
		Add_Edge(u, v, read());
	}
	
	DFS(1, 1); MN[1][0] = INF;
	for (int j=1;j<=17;j++) {
		for (int i=1;i<=n;i++) {
			fa[i][j] = fa[fa[i][j-1]][j-1];
			MN[i][j] = min(MN[i][j-1], MN[fa[i][j-1]][j-1]);
		}
	} 
	
	memset(head,0,sizeof(head));
	for (int k=read();k;k--) { 
		m = tot = read(); T = 0; 
		for (int i=1;i<=m;i++) vis[p[i] = read()] = 1;
		sort(p+1, p+1+m, CMP);
		build();
		printf("%lld\n",DP(1,1)); head[1] = 0;
		for (int i=1;i<=tot;i++) vis[p[i]] = 0, head[p[i]] = 0;
	}
	return 0;
}

【算法笔记】数位DP

昨天以专题的形式再一次加强了数位DP
主要是以zky神犇的这一篇讲稿作为主线:
http://oi.cyo.ng/wp-content/uploads/2016/10/number_dp_by_zky.ppt

这一次最主要的收获就是学到了新的代码实现方式
之前一直是以预处理+询问的方式来写代码
这样的话,对于多组询问来讲,时间复杂度还不错
但代码实现复杂度简直没法接受
且对于部分题目,这样写起来会超级麻烦

新的写法无论实在时间复杂度还是编程复杂度上都很优越
以后应该会以这种代码作为主力辣!
给个参考:http://oi.cyo.ng/?p=1519

至于解题能力的话,似乎数位DP的要求不高?
不过能和AC自动机搞一起还是挺好玩哒:http://oi.cyo.ng/?p=1534

【CodeChef FAVNUM】Favourite Numbers

题目传送门:https://www.codechef.com/problems/FAVNUM
中文题面:number_dp_by_zky

这个题目还是挺好玩的!
数位DP配上AC自动机!别有一番风味!

然而这个傻Ⅹ的AC自动机写炸了
调试了3个小时! (╯‵□′)╯︵┻━┻

#include<bits/stdc++.h>
#define LL long long
using namespace std;

char pat[20];

namespace AC_Automaton{
	#define AC AC_Automaton
	LL f[20][2000][2];
	int ch[2000][10],tag[2000],cnt,fail[2000],sta[20];
	queue<int> que;
	
	inline void insert(char *s){
		int n = strlen(s+1), w = 0;
		for (int i=1,c;i<=n;i++) {
			c = s[i] - '0';
			if (!ch[w]) {
				ch[w] = ++cnt;
			}
			w = ch[w];
		}
		tag[w] = 1;
	}
	
	inline void Build(){
		for (int i=0;i<=9;i++) if (ch[0][i]) {
			que.push(ch[0][i]);
		}
		
		while (!que.empty()) {
			int w = que.front(); que.pop();
			for (int c=0;c<=9;c++) if (ch[w]) {
				int nv = ch[w], pv = fail[w];
				while (pv && !ch[pv]) pv = fail[pv];
				fail[nv] = ch[pv];
				tag[nv] |= tag[fail[nv]];
				que.push(nv);
			} else {
				ch[w] = ch[fail[w]];
			}
		}
	}
	
	LL DFS(int t, int w, bool lim, bool leg) {
		if (!t) {
			return leg;
		} else if (!lim && ~f[t][w][leg]) {
			return f[t][w][leg];
		} else {
			LL ret = 0;
			for (int i=0,ty=lim?sta[t]:9;i<=ty;i++) {
				ret += DFS(t-1, ch[w][i], lim&&i==sta[t], leg|tag[ch[w][i]]);
			}
			return lim? ret: f[t][w][leg] = ret;
		}
	}
	
	inline LL query(LL lim) {
		int cnt = 0;
		while (lim) {
			sta[++cnt] = lim % 10;
			lim /= 10;
		}
		memset(f,-1,sizeof(f));
		return DFS(cnt,0,1,0);
	}
};

int main(){	
	LL L,R,k,n,bas,w,stp;
	cin>>L>>R>>k>>n;
	for (int i=1;i<=n;i++) {
		scanf("%s",pat+1);
		AC::insert(pat);
	}
	
	AC::Build();
	k += AC::query(L - 1);
	if (AC::query(R) < k) {
		puts("no such number");
		exit(0);
	}
	
	stp = 1; w = L - 1; 
	while (stp < (R - L + 1)) stp <<= 1;
	while (stp) {
		if (AC::query(w + stp) < k) {
			w += stp;
		}
		stp >>= 1;
	}
	printf("%lld\n",w+1);
	return 0;
}

【BZOJ 1799】[Ahoi2009] self 同类分布

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1799

就是枚举模数然后搞一搞数位DP
%NanoApe,随便减一下枝就可以Rank1了:http://nanoape.is-programmer.com/posts/193348.html

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int MOD,sta[20];
LL f[20][170][170];

LL DFS(int t, int sum, int mod, bool lim) {
	if (sum > MOD) return 0;
	else if (!t) {
		return sum == MOD && !mod;		
	} else if (!lim && ~f[t][sum][mod]) {
		return f[t][sum][mod];
	} else {
		LL ret = 0;
		for (int i=0,ty=(lim?sta[t]:9);i<=ty;i++) {
			ret += DFS(t-1, sum+i, (mod*10+i)%MOD, lim&&i==sta[t]);
		}
		return lim? ret: f[t][sum][mod] = ret;
	}
}

inline LL cal(LL lim) {
	int cnt = 0;
	while (lim) {
		sta[++cnt] = lim % 10;
		lim /= 10;
	}
	if (cnt*9 < MOD) return 0;
	else return DFS(cnt,0,0,1);
}

int main(){
	LL a,b,vout=0; cin>>a>>b;
	for (int i=1;i<=162;i++) {
		memset(f,-1,sizeof(f));
		MOD = i;
		vout += cal(b);
		vout -= cal(a-1);
	}
	printf("%lld\n",vout);
	return 0;
}

【HDU 3709】Balanced Number

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3709
中文题面:http://blog.csdn.net/to_be_better/article/details/50731499

就是枚举一下平衡点,然后dp一下
另外……
我似乎真的爱上这种递归的写法了…..
7`XEKOC~R{6W$L~J1UPP9MX

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int sta[20];
LL f[20][20][1500];

inline LL read(){
	char c=getchar(); LL ret=0;
	while (c<'0'||c>'9') c=getchar();
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret;
}

LL DFS(int t, int crt, int val, bool ruf) {
	if (val < 0) return 0;
	else if (!t) {
		return !val;
	} else if (~f[t][crt][val] && !ruf) {
		return f[t][crt][val];
	} else {
		LL ret = 0;
		for (int i=0,lim=ruf?sta[t]:9;i<=lim;i++) {
			ret += DFS(t-1,crt,val+(t-crt)*i,ruf&&i==sta[t]);
		}
		return ruf? ret: f[t][crt][val] = ret;
	}
}

inline LL cal(LL lim) {
	int cnt = 0;
	while (lim) {
		sta[++cnt] = lim % 10;
		lim /= 10;
	}
	LL ret = 0;
	for (int i=1;i<=cnt;i++) {
		ret += DFS(cnt,i,0,1);
	}
	return ret - cnt + 1;
}

int main(){
	memset(f,-1,sizeof(f));
	for (int T=read();T;--T) {
		LL l = read(), r = read();
		printf("%I64d\n",cal(r)-((l>0)?cal(l-1):0));
	}
	return 0;
}

【Codeforces 55D】Beautiful numbers

题目传送门:http://codeforces.com/contest/55/problem/D
官方题解:http://codeforces.com/blog/entry/1109
中文题面:number_dp_by_zky

之前做数位dp的题目,一直强调数位间的独立性
然而现在看来,不独立也是可以上数位dp的

题解直接看上面给的“中文题面”里的题解吧,zky的题解真的是超级棒(๑•̀ㅂ•́)و✧
我来说一说代码实现的三种方式:

1)像之前的代码一样,先预处理,然后查询的时候在数组上搞一搞
优点:对于多组询问特别好用
缺点:因为是预处理,所以对于有些题目可能很麻烦,甚至不能预处理

2)对于每一次询问单独dp,记录是否达到上限
优点:代码好写(不用于处理,直接写一个dp就成)
缺点:多组询问没法处理

3)对于每一次询问DFS,如果这一位没有受到任何限制就记忆化
这种方法是我在看这位神犇的代码的时候看到的:http://codeforces.com/contest/55/submission/3692033
优点:代码更好写,且对于多组询问相对友好(仔细想一想,他的查询次数应该不会劣于方法1?)
缺点:如果位数特别大(什么1e5,1e6之类的),则效果可能不太好

总的来说,我似乎爱上了第三种写法 = ̄ω ̄=
以后数位dp的题应该是首选第三种吧!

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int MOD[]={1,1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,90,105,120,126,140,168,180,210,252,280,315,360,420,504,630,840,1260,2520};
int pos[2530],lcm[50][50],sta[20]; 
LL f[20][50][2530];

LL GCD(LL a, LL b) {return b?GCD(b,a%b):a;}
LL LCM(LL a, LL b) {return a/GCD(a,b)*b;}


LL DFS(int t, int lm, int mod, bool top) {
	if (t == 0) {
		return !(mod % MOD[lm]);
	} else if (~f[t][lm][mod] && !top) {
		return f[t][lm][mod];	
	} else {
		LL ret = 0;
		for (int i=0,ty=(top>0?sta[t]:9),tmp;i<=ty;i++) {
			tmp = lcm[lm][i];
			ret += DFS(t-1,tmp,(mod*10+i)%2520,top&&i==sta[t]);
		}
		return top?ret:f[t][lm][mod] = ret;
	}
}

inline LL cal(LL lim) {
	int cnt = 0;
	while (lim) {
		sta[++cnt] = lim % 10;
		lim /= 10;
	}
	return DFS(cnt,1,0,1);
}

inline LL read(){
	char c=getchar(); LL ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

int main(){
	memset(f,-1,sizeof(f));
	for (int i=1;i<=48;i++) {
		pos[MOD[i]] = i;
	}
	for (int i=0;i<=48;i++) {
		for (int j=0;j<=48;j++) {
			lcm[i][j] = pos[LCM(MOD[i],MOD[j])];
		}
	} 
	for (int T=read();T;--T) {
		LL l = read(), r = read();
		printf("%I64d\n",cal(r)-cal(l-1));
	}
	return 0;
}

【BZOJ 3829】[POI2014] FarmCraft

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3829

来来来,让我们来%Claris:http://www.cnblogs.com/clrs97/p/4403170.html

#include<bits/stdc++.h>
using namespace std;

const int N = 500000+9;
const int M = 1000000+9;

int head[N],nxt[M],to[M],g[N],f[N];
int n,tmp[N],beg[N],end[N],tot,C1;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void Add_Edge(int u, int v) {
	static int T = 0;
	to[++T] = v; nxt[T] = head[u]; head[u] = T;
	to[++T] = u; nxt[T] = head[v]; head[v] = T;
}

inline bool CMP(const int &a, const int &b) {
	return max(f[a],g[a]+2+f[b]) < (f[b],g[b]+2+f[a]);
}

void DP(int w, int fa) {
	int cnt = 0;
	for (int i=head[w];i;i=nxt[i]) {
		if (to[i] != fa) cnt++;
	}
	beg[w] = tot + 1; 
	end[w] = (tot += cnt); cnt = -1;
	for (int i=head[w];i;i=nxt[i]) if (to[i] != fa) {
		DP(to[i],w);
		g[w] += g[to[i]] + 2;
		tmp[beg[w]+(++cnt)] = to[i];
	}
	if (beg[w] <= end[w]) {
		sort(tmp+beg[w],tmp+end[w]+1,CMP);
	}
	for (int i=beg[w],sum=0;i<=end[w];i++) {
		f[w] = max(f[w], f[tmp[i]] + sum + 1);
		sum += g[tmp[i]] + 2;
	}
}

int main(){
	n = read();
	for (int i=1;i<=n;i++) {
		f[i] = read();
	}
	C1 = f[1];
	for (int i=1;i<n;i++) {
		Add_Edge(read(),read());
	}
	DP(1,1);
	printf("%d\n",max(g[1]+C1,f[1]));
	return 0;
}

【BZOJ 4518】[Sdoi2016] 征途

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4518
数据生成器:http://paste.ubuntu.com/23249800/

做过玩具装箱的童鞋,看一眼就知道是斜率DP吧?

#include<bits/stdc++.h>
#define pow(x) ((x)*(x))
#define LL long long
using namespace std;

const int N = 3000+9;
const LL INF = 1e16;

LL f[N][N];
short n,m,l[N],sum,que[N][N],t1[N],t2[N];
double slope[N][N],Y[N][N];

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

int main(){ 
	n = read(); m = read();
	for (int i=1;i<=n;i++) sum += (l[i] = read()), l[i] += l[i-1];
	for (int i=0;i<m;i++) t1[i] = 1, f[n][i] = -1; t2[0] = 1;
	for (int i=1;i<=n;i++) for (int j=m-1;~j;j--) {
		if (t1[j]>t2[j]) continue; double tmp = 2.0*m*((double)m*l[i]-sum);
		while (t2[j] > t1[j] && slope[j][t1[j]] <= tmp) t1[j]++; 
		f[i][j+1] = f[que[j][t1[j]]][j] + pow(sum-(LL)m*(l[i]-l[que[j][t1[j]]]));
		tmp = f[i][j+1] + pow((double)m*l[i]); 
		while (t1[j+1] < t2[j+1] && ((tmp-Y[j+1][t2[j+1]])/(l[i]-l[que[j+1][t2[j+1]]]) <= slope[j+1][t2[j+1]-1])) t2[j+1]--;
		if (t1[j+1] <= t2[j+1]) slope[j+1][t2[j+1]] = (tmp-Y[j+1][t2[j+1]])/(l[i]-l[que[j+1][t2[j+1]]]); 
		t2[j+1]++; que[j+1][t2[j+1]] = i; Y[j+1][t2[j+1]] = tmp; 		
	} LL vout = INF;
	for (int i=1;i<=m;i++) if (~f[n][i]) vout = min(vout,f[n][i]+(LL)(m-i)*pow(sum));
	printf("%lld\n",vout/m); return 0;
}

然而再zgs的OJ上莫名其妙RE了两个点?
奥妙重重……..

【BZOJ 4513】[SDOI2016] 储能表

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4513

先来%一波Menci的强行找规律(:з」∠)https://dn-menci.qbox.me/sdoi2016-table/
然后就是std用的数位DP:http://www.ruiker1997.tk/bzoj-4513

一直感觉这个题目的数位DP很牵强
于是一直尝试使用以前的数位DP的写法来写
然后发现,嘴巴ac还是挺容易的,但真要写出来,还真心不好写
于是代码就算了吧。

—– UPD 2016.9.29 —–
还是补一份代码吧
以前数位DP都不是这么写的
不过感觉这么写,如果没有多组询问还是挺清真的

#include<bits/stdc++.h>
#define LL long long
using namespace std;

LL f[62][2][2][2],g[62][2][2][2],n,m,MOD,k; 

int main(){
	int T; cin>>T; while (T--) {
		cin>>n>>m>>k>>MOD;
		memset(f,0,sizeof(f)); memset(g,0,sizeof(g));
		g[61][0][0][0] = 1;
		for (int i=60;~i;i--) {
			int t1 = (n>>i)&1, t2 = (m>>i)&1, t3 = (k>>i)&1;
			for (int a=0;a<2;a++) for (int b=0;b<2;b++) for (int c=0;c<2;c++) if (g[i+1][a][b]) {
				for (int x=0,w,wa,wb,wc;x<2;x++) for (int y=0;y<2;y++) {
					if (w=x^y, (!a&&x>t1) || (!b&&y>t2) || (!c&&w<t3)) continue;
					wa = (a||x<t1); wb = (b||y<t2); wc = (c||w>t3);
					(g[i][wa][wb][wc] += g[i+1][a][b]) %= MOD;
					(f[i][wa][wb][wc] += ((f[i+1][a][b] + (((w-t3)*((1LL<<i)%MOD))%MOD)*g[i+1][a][b]%MOD)%MOD + MOD) % MOD) %= MOD;
				}
			}
		}	
		cout<<f[0][1][1][1]<<endl;
	}
	return 0;
}

【BZOJ 4572】[SCOI2016] 围棋

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4572

这题的轮廓线DP的做法见:http://www.cnblogs.com/clrs97/p/5452790.html
然后,我们来说一说YJQ的神奇容斥:
首先大力感谢YJQ的支持!OI温暖满人间!
具体做法:枚举每一行的情况,搞一搞dp,顺便记录一下匹配次数的奇偶
然后因为是容斥,所以我们只需要管我们规定的匹配位置,其他位置都可以随意
于是可以预处理出转移,然后搞dp

代码方面,活生生把YJQ的5k+的代码压到了2k+
自己都觉得代码不可读 QAQ

#include<bits/stdc++.h>
#define LL long long
#define abs(x) ((x)<0?-(x):(x))
using namespace std;

const int MOD = 1000000007;
const int REV = 333333336;
const int MAXN = 1 << 10;

int f[2][MAXN][2],trans[MAXN][MAXN],MX,n,m,c,q,N;
bool leg[MAXN],nxt[20][20],vis1[20],vis2[20],bit_cnt[MAXN];
char pat[2][10];

inline int pow(int w, int t) {
	int ret = 1;
	for (;t;w=(LL)w * w % MOD,t >>= 1) 
		if (t&1) ret = (LL)ret * w % MOD;
	return ret;
}

inline void prework() {
	memset(trans,0,sizeof(trans));
	for (int w=0,sign;w<=MX;w++) {
		leg[w] = sign = 1; bit_cnt[w] = __builtin_popcount(w)&1;
		for (int i=1;i<=N&&sign;i++) if (w&(1<<(i-1))) for (int j=i+1;j<=N&&sign;j++) if (w&(1<<(j-1)) && j-i < c) 
			for (int t=0;t<2&&sign;t++) for (int k=1;k<=c-j+i&&sign;k++) if (pat[t][k] != pat[t][j-i+k]) sign = leg[w] = 0;
	} 
	for (int i=1,sign;i<=N;i++) for (int j=1;j<=N;j++) {
		nxt[i][j] = sign = 1; if (abs(i-j+1) > c) continue;
		if (i < j) {for (int k=1;k<=c-j+i&&sign;k++) if (pat[0][k] != pat[1][j-i+k]) nxt[i][j] = sign = 0;}
		else for (int k=1;k<=c+j-i&&sign;k++) if (pat[1][k] != pat[0][i-j+k]) nxt[i][j] = sign = 0;
	}
	for (int w1=0;w1<=MX;w1++) if (leg[w1]) {
		memset(vis1,0,sizeof(vis1));  
		for (int i=1;i<=N;i++) if (w1&(1<<(i-1))) for (int j=0;j<c;j++) vis1[i+j] = 1;
		for (int w2=0,sign;w2<=MX;w2++) if (sign=1, leg[w2]){ 
			for (int i=1;i<=N;i++) if (w1&(1<<(i-1))) for (int j=1;j<=N;j++) if (w2&(1<<(j-1)) && !nxt[i][j]) sign = 0;
			if (!sign) continue; int tmp = 1; memset(vis2,0,sizeof(vis2));
			for (int i=1;i<=N;i++) if (w2&(1<<(i-1))) for (int j=0;j<c;j++) vis2[i+j] = 1;
			for (int i=1;i<=n;i++) if (!vis2[i]) tmp = tmp*3LL % MOD; else if (!vis1[i]) tmp = (LL)tmp*REV % MOD;
			trans[w1][w2] = tmp;
		}
	}
}

inline void solve() {
	int cur = 0, p = 1, vout = pow(3,n*m); 
	memset(f[p],0,sizeof(f[p])); f[p][0][0] = pow(3,n);
	for (int stp=2;stp<=m;stp++,cur^=1,p^=1) {
		memset(f[cur],0,sizeof(f[cur]));
		for (int w1=0;w1<=MX;w1++) for (int t=0;t<2;t++) if (f[p][w1][t]) 
			for (int w2=0;w2<=MX;w2++) (f[cur][w2][t^bit_cnt[w2]] += (LL)f[p][w1][t] * trans[w1][w2] % MOD) %= MOD;
	}
	for (int w=0;w<=MX;w++) (vout -= f[p][w][0]) %= MOD, (vout += f[p][w][1]) %= MOD;
	printf("%d\n",(vout%MOD+MOD)%MOD);
}	

int main(){
	cin>>m>>n>>c>>q;
	MX = (1 << (n - c + 1)) - 1; N = n - c + 1;
	for (int i=1;i<=q;i++) {
		scanf("%s%s",pat[0]+1,pat[1]+1);
		prework(); solve();
	} 
	return 0;
}

再次感谢YJQ的支持!容斥的做法真的好神!

【UOJ 137】[UER #3] 开学前的日历

题目传送门:http://uoj.ac/problem/137
官方题解:http://vfleaking.blog.uoj.ac/blog/714

这个题,考试的时候想到了50分的算法,但没写出来QAQ
都搞到这个程度了:
45648645
还是没有想出来,可惜啊!

70分算法的关键:\(\left( \begin{array}{l}
x\\
y
\end{array} \right) = \left( \begin{array}{l}
x – 1\\
y
\end{array} \right) + \left( \begin{array}{l}
x – 1\\
y – 1
\end{array} \right)\)

100分的关键:
看一看上面那个图,组合数实际的意义是从(u,v)走到每个点的路径的个数
于是搞一搞dp即可

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 300+9;
const int MOD = 998244353;

int f[N][N][N*2],n,m,q,X;

inline int read(){
	X = (100000005*(LL)X+20150823) % MOD;
	return X / 100;
}

int main(){
	cin>>m>>n>>q>>X;
	for (int i=1,u,v,k;i<=q;i++) {
		v = read()%m+1, u = read()%n+1, k = read()%(n+m-v-u+1);
		f[u][v][k]++;
	} 
	for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) {
		for (int k=0;k<=n+m;k++) (f[i][j][k] += (f[i-1][j][k+1] + f[i][j-1][k+1]) % MOD) %= MOD;
		(f[i][j][0] += (f[i-1][j][0] + f[i][j-1][0]) % MOD) %= MOD;
	}
	for (int j=1;j<=m;j++) for (int i=1;i<=n;i++) 
		printf("%d%c",f[i][j][0],(i==n?'\n':' '));
	return 0;
}

【NKOJ 2922】密码锁

题目传送门:http://42.247.7.121/zh/Problem/Details/2922
离线版题目:http://paste.ubuntu.com/23198035/
数据生成器:http://paste.ubuntu.com/23198038/

这题真的是好妙!点个大赞!
题解让我们召唤黄学长:http://hzwer.com/3308.html
另外,我的这份代码的复杂度多了一个k,要想速度快就去抄黄学长的代码吧!

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int n,m,k,cnt,dis[10010],sz[200],pos[30],f[1200000],cost[30][30]; 
queue<int> que;

inline int read(){
	char c=getchar(); int ret=0,f=1;
	while (c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while (c<='9'&&c>='0') {ret=ret*10+c-'0';c=getchar();}
	return ret*f;
}

inline void BFS(int W, int num) {
	memset(dis,127,sizeof(dis));
	que.push(W); dis[W] = 0;
	
	while (!que.empty()) {
		int w = que.front(); que.pop();
		for (int i=1;i<=m;i++) {
			if (w+sz[i] <= n+1 && dis[w+sz[i]] > dis[w]+1)  dis[w+sz[i]] = dis[w]+1, que.push(w+sz[i]);
			if (w-sz[i] >= 1 && dis[w-sz[i]] > dis[w]+1)  dis[w-sz[i]] = dis[w]+1, que.push(w-sz[i]);
		}
	} 
	for (int i=1;i<=cnt;i++) cost[num][i] = dis[pos[i]];
}

int main(){
	n = read(); k = read(); m = read();
	for (int i=1;i<=k;i++) dis[read()] = 1;
	for (int i=1;i<=m;i++) sz[i] = read(); sort(sz+1,sz+1+m); m = unique(sz+1,sz+1+m) - sz - 1;
	for (int i=1;i<=n+1;i++) if (dis[i] + dis[i-1] == 1) pos[++cnt] = i;
	for (int i=1;i<=cnt;i++) BFS(pos[i],i); 
	memset(f,127,sizeof(f));
	f[(1<<cnt)-1] = 0;
	for (int w=(1<<cnt)-1;w;w--) if (f[w] < 1e9) 
		for (int i=1;i<=cnt;i++) if (w&(1<<i-1)) for (int j=i+1;j<=cnt;j++) if (w&(1<<j-1) && cost[i][j] < 1e9)
			f[w^(1<<i-1)^(1<<j-1)] = min(f[w^(1<<i-1)^(1<<j-1)],f[w]+cost[i][j]); 
	printf("%d\n",f[0]>1e9?-1:f[0]);
	return 0;
}