游戏
【题目描述】
lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。
游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。
现在lxhgww想知道他最多能连续攻击boss多少次?
【输入】
输入的第一行是一个整数N,表示lxhgww拥有N种装备
接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值
【输出】
输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。
【样例输入】
3
1 2
3 2
4 5
【样例输出】
2
【数据范围】
对于30%的数据,保证N<=1000
对于100%的数据,保证N<=1000000
===========================================================================
没什么说的,匈牙利最坏10000^2过了。。其实对于这样随机的边很多的图。。匈牙利的实际运行时间比O(n)多不了多少。。实际测试表明。。比用下面方法的要快。。囧。
官方题解是:10000个点,1000000条边的一个图。对于每个联通块如果是树则只能选到n-1个,那么肯定是选最小的n-1个,如果有环则全部可以。最后for下从1开始哪些可以就行了。。
吐槽:。。。考完回来写匈牙利,写了两遍都错了,而且错的一样。。。调了很久才发现写错的地方,杯具。。
1 #include <iostream>
2 #define MAXN 1000010
3 #define MAXM MAXN*2
4
5 using namespace std;
6
7 int Count = 0;
8 int begin[MAXN+1], end[MAXM+1], next[MAXM+1];
9 void AddEdge(int a, int b){
10 Count++;
11 next[Count] = begin[a];
12 begin[a] = Count;
13 end[Count] = b;
14 }
15
16 int n;
17 #define BUFSIZE 1000000*10
18 char buf[BUFSIZE], *pt = buf;
19 #define scan_int(x) \
20 { \
21 x = 0; \
22 while (!((*pt) >= '0' && (*pt) <= '9')) pt++; \
23 while (((*pt) >= '0' && (*pt) <= '9')) x = x * 10 + (*(pt++)) - '0'; \
24 }
25 void Init(){
26 scanf("%d",&n);
27 int a,b;
28 fread(pt, 1, BUFSIZE, stdin);
29 for (int i = 1; i<=n; i++){
30 //scanf("%d%d",&a,&b);
31 scan_int(a); scan_int(b);
32 AddEdge(a,i);
33 AddEdge(b,i);
34 }
35 }
36
37 int cnt;
38 int q[MAXN+1];
39 bool hash[MAXN+1];
40 int Link[MAXN+1];
41 bool dfs(int x){
42 for (int now = begin[x]; now; now = next[now])
43 if (!hash[end[now]]){
44 hash[end[now]] = true;
45 q[++cnt] = end[now];
46 if (!Link[end[now]] || dfs(Link[end[now]])){
47 Link[end[now]] = x;
48 return true;
49 }
50 }
51 return false;
52 }
53
54 void Solve(){
55 int Ans;
56 for (int i = 1; i<=10001; i++){
57 cnt = 0;
58 if (!dfs(i)){
59 Ans = i-1;
60 break;
61 }
62 for (int j = 1; j<=cnt; j++)
63 hash[q[j]] = false;
64 }
65 printf("%d\n",Ans);
66 }
67
68 int main(){
69 freopen("game.in","r",stdin);
70 freopen("game.out","w",stdout);
71 Init();
72 Solve();
73 return 0;
74 }
75
幸运数字
【题目描述】
在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。
现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。
【输入】
输入数据是一行,包括2个数字a和b
【输出】
输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数
【样例输入1】
1 10
【样例输出1】
2
【样例输入2】
1234 4321
【样例输出2】
809
【数据范围】
对于30%的数据,保证1<=a<=b<=1000000
对于100%的数据,保证1<=a<=b<=10000000000
//================================================================
用容斥原理做。
先造出所有的幸运号码,然后对幸运号码的倍数容斥。
幸运号码有2000+个,为了尽快出解,要加几个剪枝:
1. 如果A是B的倍数,直接去掉。剪掉了一大半。。。
2.从大到小排序,尽快容斥掉一些数。
写的常数稍微少点能进2s了。。
PS :关于中间结果会爆long long的问题。。。从正的爆成负的容易,从正的爆成负的再爆成正的不容易。。。所以猥琐的判大于0。。。
1#include <iostream>
2#include <algorithm>
3#define NNUM 3000
4#define ll long long
5
6using namespace std;
7
8ll A,B;
9void Init(){
10 scanf("%I64d%I64d",&A,&B);
11}
12
13int n = 0;
14ll a[NNUM+1];
15void dfsNum(ll num){
16 if (num > B) return;
17 if (num <= B)
18 a[++n] = num;
19 dfsNum(num * (ll)10 + (ll)6);
20 dfsNum(num * (ll)10 + (ll)8);
21}
22int cnt = 0;
23ll b[NNUM+1];
24
25ll Ans = 0, tmp;
26inline ll gcd(ll a, ll b){
27 while (b)
28 tmp = a, a = b, b = tmp % b;
29 return a;
30}
31
32
33int cmp(const void *a, const void *b){
34 return (*(ll *)b) > (*(ll *)a) ? 1 : -1;
35}
36
37ll dfs(int pos, ll num){
38 if (pos == n+1) return B/num - A/num;
39 ll ret = dfs(pos+1, num);
40 ll newnum = num / gcd(num, a[pos]) * a[pos];
41 if (newnum <= B && newnum >= 1)
42 ret -= dfs(pos+1, newnum);
43 return ret;
44}
45
46void Solve(){
47 dfsNum(6); dfsNum(8);
48 qsort(a+1, n, sizeof(a[0]), cmp);
49 //printf("%d\n",n);
50 for (int i = 1; i<=n; i++){
51 bool boo = true;
52 for (int j = 1; j<=n; j++)
53 if (i!=j && a[i] % a[j] == 0){
54 boo = false;
55 break;
56 }
57 if (boo){
58 a[++cnt] = a[i];
59 //printf("%d %I64d\n", cnt, a[cnt]);
60 }
61 }
62 n = cnt;
63 //printf("%d\n",n);
64 A--;
65 printf("%I64d\n", B - A - dfs(1,1));
66}
67
68int main(){
69 freopen("luckynumber.in","r",stdin);
70 freopen("luckynumber.out","w",stdout);
71 Init();
72 Solve();
73 return 0;
74}
75
timtopcoder.wep.dk
新开的网站,欢迎大家捧场~
摘要: 最开始写费用流的时候,有且只会SPFA版的费用流,而且一直都够用,一般来说只要建出了图就赢了,网络流怎么都不会超时。。。。。这个情况到今天被终结了。。。终结者见下:--------------------------------------------------------------------------------------------------------
最优图像
【题目描述】...
阅读全文
摘要: 题目叙述: 给出一个有n个节点的树, 一个整数k. 定义dist(u, v)为节点u, v间的距离. 求这棵树中有多少节点对(u, v)满足dist(u, v)<=k. (n<=10000)-------------------------------------------对于这样一个数据范围,显然O(n^2)的直接算是会超时的。大体的思路是:&n...
阅读全文
This is a reminder of what I need to learn and need to do.
割点和桥
带上下界的网络流:可行流,最大流及其费用流
polya定理
做道要用逆元的题
斯特灵数:第一类,第二类
NOIP2009最后一题。
看到题的时候直接就想到了DancingLinks,但是。。。很后悔是,原来想学的时候觉得难写就没写了。
不过考试时裸搜加最优性剪枝有95分,也很不错了。
DacingLinks其实就是十字链表,用于求解精确覆盖问题:对于一个0-1矩阵,选取一些行使得每列有且只有一个1。
把数独转换为这样一个模型以后就可以用DacingLinks快速的搜索了。
搜索时每次选择1的个数最少的那列,枚举那列上选取的某行,再把那行其他位置有1的列删除,接着继续搜索。回溯时再还原改动。
对于数独而言,一共有9*9个格子,每个格子可以填9个数,所以在0-1矩阵里就有9*9*9=729行,表示在某个格子里填某个数。
同时在某个位置填了某个数后,那个数所在的列,行,9宫格也不能填那个数了,同时那个格子也不能填其他数了,所以某行填某个数有9*9,某列填某个数有9*9,某个9宫格填某个数9*9,某个位置填数9*9,一共就用324列。
建好这样一个图后,就直接用DancingLinks搜索,因为相比一般的裸搜冗余很少,所以速度非常快。
/*
* $File: sudoku.cpp
* $Date: Sun Nov 29 20:22:32 2009 CST
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define LENGTH 9
#define SQRLEN 3
#define MAXN (LENGTH*LENGTH*LENGTH)
#define MAXM (4*LENGTH*LENGTH)
#define MAXNODE MAXN*MAXM
int max(int a,int b){
return a>b?a:b;
}
int map[MAXN][MAXM];
int U[MAXNODE],D[MAXNODE],L[MAXNODE],R[MAXNODE];
int S[MAXNODE],C[MAXNODE],ROW[MAXNODE];
int n,m;
int h = 0;//the Leftest and Upest node
int a[LENGTH][LENGTH];
void Init(){
freopen("sudoku.in","r",stdin);
freopen("sudoku.out","w",stdout);
for (int i = 0; i<LENGTH; i++)
for (int j = 0; j<LENGTH; j++)
scanf("%d",&a[i][j]);
}
int Row(int x,int y,int num){
return (x*LENGTH+y)*LENGTH+num-1;
}
#define SEC_POS 0
#define SEC_ROW 1
#define SEC_COL 2
#define SEC_SQR 3
#define PER_SEC LENGTH*LENGTH
void Fill(int x,int y,int num){
int row = Row(x,y,num);
map[row][SEC_POS*PER_SEC+x*LENGTH+y] = 1;
map[row][SEC_ROW*PER_SEC+x*LENGTH+num-1] = 1;
map[row][SEC_COL*PER_SEC+y*LENGTH+num-1] = 1;
map[row][SEC_SQR*PER_SEC+((x/SQRLEN)*SQRLEN+(y/SQRLEN))*LENGTH+num-1] = 1;
}
int cnt;
void BuildGraph(){
// Build The 0-1 Matrix
for (int i = 0; i<LENGTH; i++)
for (int j = 0; j<LENGTH; j++)
if (a[i][j])
Fill(i,j,a[i][j]);
else for (int k = 1; k<=LENGTH; k++)
Fill(i,j,k);
// Build Dacing Links
n = MAXN,m = MAXM;
for (int i = 0; i<n; i++)
for (int j = 0; j<m; j++)
if (map[i][j])
map[i][j] = ++cnt;
int tmp,s = 0,t = 0;
for (int i = 0; i<n; i++){
for (int j = 0; j<m; j++)
if (tmp=map[i][j])
L[tmp] = t, S[tmp] = i,t = tmp;
for (int j = m-1; j>=0; j--)
if (tmp=map[i][j])
R[tmp] = s, s =tmp;
R[t] = s,L[s] = t;
}
for (int j = 0; j<m; j++){
t = ++cnt;
for (int i = 0; i<n; i++)
if (tmp=map[i][j])
U[tmp] = t, t = tmp,C[tmp] = cnt, ++S[cnt];
s = cnt;
for (int i = n-1; i>=0; i--)
if (tmp=map[i][j])
D[tmp] = s, s = tmp;
D[cnt] = s,U[cnt] = t;
}
for (int i = cnt-m+1; i<=cnt; i++)
L[i] = i-1;
for (int i = cnt; i>cnt-m; i--)
R[i] = i+1;
R[h] = cnt-m+1,L[h] = cnt;
L[cnt-m+1] = R[cnt] = h;
}
int ans[MAXM+1];
void Cover(int c){
L[R[c]] = L[c],R[L[c]] = R[c];
for (int i = D[c];i!=c;i = D[i])
for (int j = R[i];j!=i;j = R[j])
U[D[j]] = U[j],D[U[j]] = D[j],S[C[j]]--;
}
void UnCover(int c){
for (int i = U[c];i!=c;i=U[i])
for (int j = L[i];j!=i;j = L[j])
S[C[j]]++,U[D[j]] = D[U[j]] = j;
L[R[c]] = R[L[c]] = c;
}
int Ans = -1;
int ScoreTable[LENGTH][LENGTH] = {
{6,6,6,6,6,6,6,6,6},
{6,7,7,7,7,7,7,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,9,10,9,8,7,6},
{6,7,8,9,9,9,8,7,6},
{6,7,8,8,8,8,8,7,6},
{6,7,7,7,7,7,7,7,6},
{6,6,6,6,6,6,6,6,6}
};
int score(int c){
int t = S[c];
int num = t%LENGTH+1;
int x = t/LENGTH/LENGTH%LENGTH;
int y = t/LENGTH%LENGTH;
return num*ScoreTable[x][y];
}
int ansmap[LENGTH][LENGTH];
//this function is not used in this program, but it gives out a solution of a sudoku
void GetAns(int step){
memset(ansmap,0,sizeof(ansmap));
for (int i = 0; i<step; i++){
int t = ans[i];
int x = t/LENGTH/LENGTH%LENGTH;
int y = t/LENGTH%LENGTH;
int num = t%LENGTH+1;
ansmap[x][y] = num;
}
}
void search(int step,int v){
if (R[h] == h){
Ans = max(Ans,v);
/* GetAns(step);
for (int i = 0; i<LENGTH; i++){
for (int j = 0; j<LENGTH; j++)
printf("%d ",ansmap[i][j]);
printf("\n");
}
printf("\n");*/
return;
}
int c,s = MAXNODE;
for (int i = R[h];i!=h; i=R[i])
if (S[i]<s)
s = S[i],c = i;
Cover(c);
for (int i = D[c];i!=c;i=D[i]){
ans[step] = S[i];
for (int j = R[i];j!=i;j = R[j])
Cover(C[j]);
search(step+1,v+score(i));
for (int j = L[i];j!=i;j = L[j])
UnCover(C[j]);
}
UnCover(c);
}
void DancingLinks(){
search(0,0);
printf("%d\n",Ans);
}
void Solve(){
BuildGraph();
DancingLinks();
}
int main(){
Init();
Solve();
return 0;
}
前两天在yjw神牛和hyf神牛的共同努力下终于学会了后缀数组O(nlogn)倍增构造方法。
构造:
定义s[k][i]表示从s字符串的第i位开始的长度为k的一个字符串(后缀),不够的补零,sa[k][i]表示在所有长度为k的后缀中排序后大小为第i的后缀的位置。显然sa[1]可以直接qsort得到。当sa[k]求到过后,sa[2k]的每个元素都可以O(1)比较得出,这时用桶排,把sa[k]中排名相同的放在一起(放在一个桶里),那么对于每个不同的桶中的元素,他们之间的大小关系就已经确定了,对于同一个桶中的元素,s[2k][i]的前k位是一样的,可能不一样只有后k位,而这个我们是已经得到了的,所以通过sa[k][i]可以算出sa[2k][i-k],桶排把排序过程复杂度降成了O(n),总体构造的复杂度就成了O(nlogn)。DC3算法貌似可以把复杂度降到O(n)...暂时只有膜拜的份了。
现在定义sa[i]为所有后缀排好序后的第i个后缀在原序列中的位置。
定义rank[i]为后缀s[i]在后缀排好序的序列中的排名。
当sa数组求出来了过后,就可以构造h和height数组了。
定义height数组为排好序的后缀中相邻两项的LCP(最常公共前缀),即height[i] = LCP(sa[i],sa[i-1])。
定义h数组为原序列中的某个后缀与排好序的后缀中它的前一个的LCP,即h[i] = LCP(i,sa[rank[i]-1])。
然后有一个hyf和yjw神牛都不知道怎么来的很牛X的定理:h[i]>=h[i-1]-1。。。所以就可以在O(n)时间内直接for出这个h数组来。。。(这步是最诡异也最精髓的,因为没有这个数组后缀数组就没什么用了。。求神牛们讲解下这个定理的证明。。)
求出了h数组后height数组也可以直接得到。
实现方式是用了两个指针轮番上阵,看起来可能会有点纠结。。
应用:
有了h和height数组后,我们就可以用它来做很多事情。但我还不是很熟,只会求一个字符串里面可以相交的最长重复字串的长度。。
cii(uva?)3901 Editor就是这样一道题。
比如abcabcabc的最长重复字串就是abcabc。
其实求出了h和height数组后就变得很简单,就是h或height数组中最大的一个。
欢迎大牛神牛们前来补充和指正!
1 /*
2 * $Date: Fri Nov 27 09:00:37 2009 CST
3 * $File: 3901.cpp
4 */
5 #include <iostream>
6 #include <cstdio>
7 #include <cstdlib>
8 #include <cstring>
9 #include <algorithm>
10 #define MAXLEN 50000
11
12 using namespace std;
13
14 char s[MAXLEN+1];
15 bool cmp(const int &a,const int &b){
16 return s[a]<s[b];
17 }
18
19 int sa[MAXLEN+1],rank[MAXLEN+1],tmp1[MAXLEN+1],tmp2[MAXLEN+1];
20 int h[MAXLEN+1],height[MAXLEN+1];
21
22 void Solve(){
23 scanf("%s",s);
24 int n = strlen(s);
25 s[n++] = 0;
26 for (int i = 0; i<n; i++)
27 sa[i] = i;
28 sort(sa,sa+n,cmp);
29
30 rank[sa[0]] = 0;
31 for (int i = 1; i<n; i++)
32 if (s[sa[i]]==s[sa[i-1]])
33 rank[sa[i]] = rank[sa[i-1]];
34 else
35 rank[sa[i]] = rank[sa[i-1]]+1;
36
37 int *s1 = sa,*s2 = tmp1,*b = tmp2, *r = rank;
38 for (int i = 1; i<n&&r[sa[n-1]]<n-1; i<<=1){
39 //b is the barrel
40 for (int j = 0; j<n; j++)
41 b[r[s1[j]]] = j;
42 for (int j = n-1; j>=0; j--)
43 if (s1[j]>=i)
44 s2[b[r[s1[j]-i]]--] = s1[j]-i;
45 for (int j = n-i; j<n; j++)
46 s2[b[r[j]]] = j;
47 swap(s1,s2);
48 b[s1[0]] = 0;//cause the barrel is now useless, use b as the new rank array
49 for (int j = 1; j<n; j++)
50 if (r[s1[j]]!=r[s1[j-1]]||r[s1[j]+i]!=r[s1[j-1]+i])
51 b[s1[j]] = b[s1[j-1]]+1;
52 else
53 b[s1[j]] = b[s1[j-1]];
54 swap(b,r);
55 }
56 //calc
57 for (int i = 0; i<n; i++){
58 if (r[i] == 0)
59 h[i] = 0;
60 else if (i == 0||h[i-1] == 0)
61 for (h[i] = 0; s[i+h[i]]==s[s1[r[i]-1]+h[i]];h[i]++);
62 else
63 for (h[i]=h[i-1]-1 ; s[i+h[i]]==s[s1[r[i]-1]+h[i]];h[i]++);
64 }
65 int Ans = 1;
66 for (int i = 0; i<n; i++)
67 Ans = max(Ans,h[i]);
68 printf("%d\n",Ans);
69 }
70 int main(){
71 int t;
72 scanf("%d",&t);
73 while (t--)
74 Solve();
75 return 0;
76 }
77
摘要: Dynamic Rankings
Time Limit: 10 Seconds
Memory Limit: 32768 KB
The Company Dynamic Rankings has developed a new kind of computer that is no
longer satisfied wit...
阅读全文
学习某三位神牛。。在cppblog安个家,发点心得体会,一来作为自己的一个复习备忘录,二来给更多的人分享知识~