游戲
【題目描述】
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
6
using namespace std;
7
8
ll A,B;
9
void Init()
{
10
scanf("%I64d%I64d",&A,&B);
11
}
12
13
int n = 0;
14
ll a[NNUM+1];
15
void 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
}
22
int cnt = 0;
23
ll b[NNUM+1];
24
25
ll Ans = 0, tmp;
26
inline ll gcd(ll a, ll b)
{
27
while (b)
28
tmp = a, a = b, b = tmp % b;
29
return a;
30
}
31
32
33
int cmp(const void *a, const void *b)
{
34
return (*(ll *)b) > (*(ll *)a) ? 1 : -1;
35
}
36
37
ll 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
46
void 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
68
int 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安個家,發點心得體會,一來作為自己的一個復習備忘錄,二來給更多的人分享知識~