Hey,Lucida is back.

CS


  • 首页

  • 归档

  • friends

  • 搜索

Codeforces Round

发表于 2017-02-27 | 更新于 2018-06-18

除了F题都比较水吧。。F题Div2只有一个在结束之前2minAC的。。

A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}

const int MAXN=100+11;

int main()
{
static int a[6],b[6];
int n;get(n);
for(int i=1;i<=n;++i)
{
int x;get(x);
a[x]++;
}
for(int i=1;i<=n;++i)
{
int x;get(x);
b[x]++;
}
int Ans=0;
for(int i=1;i<=5;++i)
if((a[i]+b[i])&1)
{
Ans=-1;
break;
}
else
Ans+=abs(b[i]-a[i])/2;
put(Ans==-1?Ans:Ans/2),putchar('\n');
return 0;
}

B

写完E题回来看发现有个细节可能被hack,改掉交上之后从rnk30直接rnk100。。好吧,关键是比赛之后交之前的版本居然A了,,感觉非常。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
int divider;
bool check(char *s,int S,int n)
{
ll x=0;
int i=0;
while(i<=n && !((S>>i)&1)) i++;
if(s[i]=='0' || i>=n) return 0;
for(;i<=n;++i)
if((S>>i)&1)
(x*=10)+=s[i]-'0';
return x%divider==0;
}
int popcount(int S)
{
int cnt=0;
while(S)
{
cnt++;
S-=(S & -S);
}
return cnt;
}
int main()
{
char n[100];scanf("%s",n);
int k;get(k);
int len=strlen(n);
int Ans=len-1;
divider=1;
for(int i=1;i<=k;++i) divider*=10;
for(int S=(1<<len)-1;~S;S--)
{
//printf("%d\n",S);
if(check(n,S,len))
chkmn(Ans,len-popcount(S));
}
put(Ans),putchar('\n');
return 0;
}

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}

const int MAXN=2e5+11;
using std::sort;
struct Obj
{
int a,b,d;
bool operator <(const Obj &x)const {return d<x.d;}
}p[MAXN];
int main()
{
int n,k;get(n),get(k);
for(int i=1;i<=n;++i)
get(p[i].a);
for(int i=1;i<=n;++i)
get(p[i].b);
int Ans=0;
for(int i=1;i<=n;++i)
{
p[i].d=p[i].a-p[i].b;
Ans+=p[i].b;
}
sort(p+1,p+1+n);
for(int i=1;i<=n;++i)
{
if(i>k && p[i].d>=0)
break;
Ans+=p[i].d;
}
put(Ans),putchar('\n');
return 0;
}

D

二分答案。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}

const int MAXN=200000+11;
char s[MAXN],t[MAXN];
int opt[MAXN],ns,nt;
bool check(int step)
{
static int us[MAXN],ut;
++ut;
for(int i=1;i<=step;++i) us[opt[i]]=ut;
for(int i=1,j=1;i<=ns;++i)
{
if(us[i]==ut) continue;
if(s[i]==t[j]) j++;
if(j==nt+1) return 1;
}
return 0;
}
int main()
{
scanf("%s",s+1);
scanf("%s",t+1);
ns=strlen(s+1),nt=strlen(t+1);
for(int i=1;i<=ns;++i) get(opt[i]);
int L=0,R=ns;
while(L!=R)
{
int Mid=(L+R+1)>>1;
if(check(Mid)) L=Mid;
else R=Mid-1;
}
put(L),putchar('\n');
return 0;
}

E

按位贪心..就是输入有点烦..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
using std::string;
using std::map;
using std::cin;
using std::cout;
using std::bitset;

const int MAXN=5000+11,MAXBIT=1e3+11;
struct Var
{
int a,b;char opt;
bitset<MAXBIT> num;
}var[MAXN];
map<string,int> ref;
string name[MAXN];
void b2t(string s,bitset<MAXBIT> &b)
{
for(unsigned int i=0;i<s.size();++i)
b[i]=s[i]-'0';
}
int us[MAXN],ut;
bool calc(int i,int b)
{
static bool val[MAXN];
if(us[i]==ut) return val[i];
us[i]=ut;
if(var[i].opt==0)
return val[i]=var[i].num[b];
else
{
switch(var[i].opt)
{
case 'A':val[i]=calc(var[i].a,b) & calc(var[i].b,b);break;
case 'O':val[i]=calc(var[i].a,b) | calc(var[i].b,b);break;
case 'X':val[i]=calc(var[i].a,b) ^ calc(var[i].b,b);break;
}
return val[i];
}
}
int n;
int count(bool v,int b)
{
++ut;
var[0].num[b]=v;
int cnt=0;
for(int i=1;i<=n;++i)
cnt+=calc(i,b);
return cnt;
}
int main()
{
ref["?"]=0;
int m;get(n),get(m);
string opt;
for(int i=1;i<=n;++i)
{
cin>>name[i];
ref[name[i]]=i;
cin>>opt>>opt;
if(isdigit(opt[0]))
{
b2t(opt,var[i].num);
var[i].opt=0;
}
else
{
var[i].a=ref[opt];
cin>>opt;
var[i].opt=opt[0];
cin>>opt;
var[i].b=ref[opt];
}
}
static char mx[MAXBIT],mn[MAXBIT];
for(int i=m-1;~i;--i)
{
int c0=count(0,i),c1=count(1,i);
if(c0==c1) mx[i]=mn[i]='0';
else if(c0>c1) mx[i]='0',mn[i]='1';
else if(c0<c1) mn[i]='0',mx[i]='1';
else assert(0);
}
puts(mn),puts(mx);
return 0;
}

F

研究了zky大神的代码好长时间才差不多明白(人弱求不D) 思路大概是Trie的合并 枚举每个点,把这个点的子节点的Trie合并起来,看合并之后的子树大小。同一层的合并一定是互不影响的。 胡扯一下复杂度? 最坏情况是每次都要合并,合并的最坏复杂度是O(n2O(\dfrac n2O(​2​​n​​)所以T(n)=2T(n2)+O(n2)T(n)=2T(\dfrac n2)+O(\dfrac n2)T(n)=2T(​2​​n​​)+O(​2​​n​​) 求教复杂度。。。。

还有其他做法啊。。待学习。。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
using std::min_element;
const int MAXN=3e5+11;
int dep[MAXN],sz[MAXN],next[MAXN][26];
void DFS(int pos)
{
sz[pos]=1;
for(int i=0;i<26;++i)
if(next[pos][i])
{
int u=next[pos][i];
dep[u]=dep[pos]+1;
DFS(u);
sz[pos]+=sz[u];
}
}
int Cal(int *a,int ac)
{
if(ac==0) return 0;
if(ac==1)
return sz[a[1]];
int res=1;
for(int c=0;c<26;++c)
{
int b[27],bc=0;
for(int i=1;i<=ac;++i)
if(!(b[++bc]=next[a[i]][c])) bc--;
res+=Cal(b,bc);
}
return res;
}
int main()
{
int n;get(n);
for(int i=2;i<=n;++i)
{
int u,v,x;get(u),get(v);
while(!isalpha(x=getchar()));
next[u][x-'a']=v;
}
DFS((dep[1]=1,1));
static int Ans[MAXN],cnt[MAXN];
for(int pos=1;pos<=n;++pos)
{
int a[27],ac=0;
for(int c=0;c<26;++c)
if(!(a[++ac]=next[pos][c])) ac--;
cnt[pos]=ac?Cal(a,ac):1;
}
for(int i=1;i<=n;++i) Ans[i]=n;
for(int pos=1;pos<=n;++pos) Ans[dep[pos]]+=cnt[pos]-sz[pos];
int p=min_element(Ans+1,Ans+1+n)-Ans;
put(Ans[p]),putchar('\n');
put(p),putchar('\n');
return 0;
}

BZOJ 1004 Cards

发表于 2017-02-26 | 更新于 2018-06-18

Link

Wiki上说Burnside引理的公式最早是柯西发现的?让我想起了阿拉伯数字有木有

Solution

部分来自NoAlGo

群

一个集合GGG,在GGG上定义了二元运算∗*∗满足以下条件

  1. 封闭性,即任意a∈G,b∈Ga\in G,b\in Ga∈G,b∈G,有a∗b∈Ga*b\in Ga∗b∈G。
  2. 结合律,(a∗b)∗c=a∗(b∗c)(a*b)*c=a*(b*c)(a∗b)∗c=a∗(b∗c)。
  3. 存在单位元eee。对任意a∈Ga\in Ga∈G,a∗e=e∗a=aa*e=e*a=aa∗e=e∗a=a。
  4. 存在逆元。对任意元素aaa,存在bbb,使得a∗b=b∗a=ea*b=b*a=ea∗b=b∗a=e。

则称GGG是对于运算∗*∗的群。

Burnside引理

GGG是 上的置换群, ,记f(ai)f(a_i)f(a​i​​)为置换aia_ia​i​​的不动点个数,则GGG在XXX上的不同染色方案数目为 1∣G∣∑f(ai)\dfrac {1}{|G|}\sum f(a_i)​∣G∣​​1​​∑f(a​i​​)

然后看题目

  1. 保证任意多次洗牌都可用这 m种洗牌法中的一种代替
  2. 对每种洗牌法,都存在一种洗牌法使得能回到原状态

就满足了群的条件1,2,4,还有一个条件3需要满足,那就添加一个置换am+1={bi,bi=i}a_{m+1}=\{b_i,b_i=i\}a​m+1​​={b​i​​,b​i​​=i}

然后就可以上Burnside引理了。

Tips

用各种定理都要牢记适用条件,不能只记结论随便用。比如古代猪文。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
const int MAXB=25,MAXN=MAXB*3;
int P;
int Pow(int base,int v,int P)
{
int res=1;
while(v)
{
if(v&1)
(res*=base)%=P;
(base*=base)%=P;
v>>=1;
}
return res;
}
int inv(int a,int P)
{
return Pow(a,P-2,P);
}
int Calc(int *a,int n,int R,int B,int G)
{
static int cyc[MAXN],cc;
static bool ud[MAXN];
cc=0;
memset(ud,0,sizeof(ud));
for(int i=1;i<=n;++i)
{
if(ud[i]) continue;
int l=0;
while(!ud[i])
{
l++;
ud[i]=1;
i=a[i];
}
cyc[++cc]=l;
}
//用x个体积为v_i的物品 填3个给定大小的背包的方案数目
static int f[MAXN][MAXB][MAXB][MAXB];
memset(f,0,sizeof(f));
f[0][0][0][0]=1;
for(int i=1;i<=cc;++i)
for(int r=0;r<=R;++r)
for(int b=0;b<=B;++b)
for(int g=0;g<=G;++g)
{
if(cyc[i]<=r)
(f[i][r][b][g]+=f[i-1][r-cyc[i]][b][g])%=P;
if(cyc[i]<=b)
(f[i][r][b][g]+=f[i-1][r][b-cyc[i]][g])%=P;
if(cyc[i]<=g)
(f[i][r][b][g]+=f[i-1][r][b][g-cyc[i]])%=P;
}
return f[n][R][B][G];
}
int main()
{
static int a[MAXN];
int R,B,G,m;
get(R),get(B),get(G),get(m),get(P);
int n=R+B+G;
for(int i=1;i<=n;++i)
a[i]=i;
int Ans=Calc(a,n,R,B,G);
for(int i=1;i<=m;++i)
{
for(int j=1;j<=n;++j) get(a[j]);
(Ans+=Calc(a,n,R,B,G))%=P;
}
(Ans*=inv(m+1,P))%=P;
put(Ans),putchar('\n');
return 0;
}

胡扯Calalan数

发表于 2017-02-26 | 更新于 2018-06-18

Most Important Formulas

以下部分来自http://lanqi.org/skills/10939/

  1. C(n)=C2nn−C2nn−1C(n)=C_{2n}^n-C_{2n}^{n-1}C(n)=C​2n​n​​−C​2n​n−1​​

    Problem

    对于{1,2,..,n}\{1,2,..,n\}{1,2,..,n}的进栈序列,有多少合法的出栈序列。

    Solution

    把进出栈看作事件,进栈记111,出栈记−1-1−1,则一个事件序列的任意前缀和都≥0\ge 0≥0 首先有nnn个111,先不管是否合法,只管总个数,那么序列的数目为C2nnC_{2n}^nC​2n​n​​ 要从中减去不合法的,这样考虑: 每个不合法的序列,最先不合法的位置iii,一定是前缀和为−1-1−1,−1-1−1比111多了一个 那么把1..i1..i1..i的数字全部取反,整个序列就有n+1n+1n+1个111,n−1n-1n−1个−1-1−1。 记不合法的方案集合为III,存在n+1n+1n+1个111,n−1n-1n−1个−1-1−1的序列集合为SSS, 现在要证明的是III中的元素与SSS中的元素构成一一映射。 首先,∀i∈I\forall i \in I∀i∈I,可以转换为唯一的s∈Ss \in Ss∈S 而对于∀s∈S\forall s \in S∀s∈S,如果从第二个前缀和为111的位置jjj开始取反,那取反后第一个前缀和为−1-1−1的位置必然不是jjj,那这种不合法方案转化出对应的s′∈S≠ss' \in S \neq ss​′​​∈S≠s,所以∀s∈S\forall s\in S∀s∈S一定唯一对应i∈Ii\in Ii∈I。 即不合法的方案数为C2nn−1C_{2n}^{n-1}C​2n​n−1​​

    Another Example

    (搜到的一个解说三角划分方案数的图,似乎来自一个被封的csdn博客) 这样可以把三角划分转化为划分序列连乘顺序 序列连乘顺序可以转化为合法括号序列数目 然后就可以解决了

    Tips

    Catalan数是用映射来解决计数问题的。当有某种属性的方案不好统计的时候,就把这种方案的集合与其它比较容易统计的方案建立一一映射。这种转化思路大概即为其巧妙之处吧。

  2. C(n+1)=∑i=0nC(i)C(n−i),C(0)=1C(n+1)=\sum\limits_{i=0}^nC(i)C(n-i),C(0)=1C(n+1)=​i=0​∑​n​​C(i)C(n−i),C(0)=1

    Problem

    圆周上有2n2n2n个点,以这些点为端点连互不相交的nnn条弦,求不同连法的总数

    Solution

    考虑DP。 设f(i)f(i)f(i)表示iii个点时的答案,则f(0)=1f(0)=1f(0)=1,答案为f(2n)f(2n)f(2n)。 显然,任意弦的两端点之间的优弧和劣弧上都一定有偶数个点 所以,f(i)=f(0)f(i−2)+f(2)f(i−4)+f(4)f(i−6)+..+f(i−2)f(2)f(i)=f(0)f(i-2)+f(2)f(i-4)+f(4)f(i-6)+..+f(i-2)f(2)f(i)=f(0)f(i−2)+f(2)f(i−4)+f(4)f(i−6)+..+f(i−2)f(2) 将2n2n2n代入,f(2n)=f(0)f(2n−2)+f(2)f(2n−4)+f(4)f(2n−6)+..+f(2n−2)f(0)f(2n)=f(0)f(2n-2)+f(2)f(2n-4)+f(4)f(2n-6)+..+f(2n-2)f(0)f(2n)=f(0)f(2n−2)+f(2)f(2n−4)+f(4)f(2n−6)+..+f(2n−2)f(0) 考虑Catalan数的递推式 C(n)=C(0)C(n−1)+C(1)C(n−2)+C(2)C(n−3)+...+C(n−1)C(0)C(n)=C(0)C(n-1)+C(1)C(n-2)+C(2)C(n-3)+...+C(n-1)C(0)C(n)=C(0)C(n−1)+C(1)C(n−2)+C(2)C(n−3)+...+C(n−1)C(0) 可以看出f(2n)==C(n)f(2n)==C(n)f(2n)==C(n)

Other Examples

  1. nnn个顶点的不同二叉树的个数是C(n)C(n)C(n)。
  2. 2n2n2n个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,排列方式有C(n)C(n)C(n)种
  3. 设a1,a2,...,ana_1,a_2,...,a_na​1​​,a​2​​,...,a​n​​为整数,且满足下列条件:
    • a1≤1,a2≤2,...,an≤na_1\le 1,a_2\le 2,...,a_n\le na​1​​≤1,a​2​​≤2,...,a​n​​≤n 则a1,a2,...,ana_1,a_2,...,a_na​1​​,a​2​​,...,a​n​​满足上述条件的序列的个数是C(n)C(n)C(n)
  4. 设a1,a2,...,ana_1,a_2,...,a_na​1​​,a​2​​,...,a​n​​与b1,b2,...,bnb_1,b_2,...,b_nb​1​​,b​2​​,...,b​n​​是两个完全不同的序列,则把这两个序列归并组成一个新的序列,恰有kkk个逆序的方案数是C(n)C(n)C(n)(一个逆序是指aia_ia​i​​排在bib_ib​i​​的后面)。(想了很长时间不知道怎么证明)

BZOJ 3153 Sone1

发表于 2017-02-26 | 更新于 2018-06-18

Link

Solution

AAA树的模板题。然而调了很长时间

关于标记

在一个数据结构中,对于一条信息,不可以同时存在两个功能相同的标记覆盖到它,因为这样会使标记合并的过程紊乱。好吧我知道这是常识但我确确实实刚刚发现 对于 的标记,因为需要同时修改链和子树,所以需要两种标记。 首先链的标记mchmchmch是没跑了,其次还需要一个修改子树的标记。 在翱犇和zky的论文和代码中,发现他们都是维护了子树中除了链之外的部分的标记mremremre。我想不通为什么,而且我还用直接记录整个子树的标记mallmallmall的方法AC了BZOJ3786,也没有发现什么问题。 于是我就写了mch+mallmch+mallmch+mall。 改到最后解决其它的所有问题之后狂WA不止。想了很长时间才领悟到这个十分显然的道理:mch,mallmch,mallmch,mall都修改了链的信息,那先来后到就不好安排了。虽然感性地认知一下,应该可以通过一些比较复杂的分类讨论搞定,但我还是选择了改成mch+mremch+mremch+mre。

关于标记下传

写 的时候总结出一个原则:只有在树的结构修改的时候才需要 ,这样就避免了通过DFS或者手工栈预先下传一条链的标记(虽然复杂度不受影响但是我觉得代码不优美) 在 ,其他的部分也可以遵循这个原则从而避免DFS,但是有一个地方是没有办法的:在删除虚子树的时候。 因为在虚子树的 中,只能旋转虚节点。当一个点要被切掉的时候,必须把上方所有的标记都传过来。如果可以转它的话就好办了,但问题就出在这。 所以经过很长时间的思考,我只好接受了这个现实:从待删除虚子树到其真正父节点的路径上的标记只能手动下传。这个过程还要注意:一定要先向上跳一下到一个虚节点上,再开始不断向上跳。否则的话根本不会向上走。 还有一个坑点:在存在revrevrev标记的 中,自上而下查找也必须要 ,因为revrevrev前后树是完全不同的,不 就会找到错误的节点。

Code

同样的东西,人家写200+,我写400+。吐槽自己一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
using std::max;
using std::min;
using std::pair;
using std::make_pair;
using std::fill;
using std::swap;
const int MAXN=100000+11,INF=INT_MAX;

struct Mark
{
int k,b;
Mark(int k=1,int b=0):k(k),b(b){}
int operator ()(int x,int sz=1) {return k*x+sz*b;}
Mark &operator +=(Mark a)
{
int dk=k*a.k,db=b*a.k+a.b;
k=dk,b=db;
return *this;
}
bool operator !=(Mark a) {return k!=a.k || b!=a.b;}
}nMark;
struct Data
{
int minv,maxv,sumv,sz;
Data(int minv=INF,int maxv=-INF,int sumv=0,int sz=0):minv(minv),maxv(maxv),sumv(sumv),sz(sz){}
Data &operator +=(Mark b)
{
if(sz)
{
minv=b(minv);
maxv=b(maxv);
sumv=b(sumv,sz);
}
return *this;
}
}nData;

Data operator +(Data a,Data b)
{
return Data(min(a.minv,b.minv),
max(a.maxv,b.maxv),
a.sumv+b.sumv,
a.sz+b.sz);
}
namespace AAA
{
const int SIZE=MAXN<<2;
struct Node
{
Node *son[4],*fa;
Data se,re,ch,all;
Mark mch,mre;
bool rev,imag;
int kind()
{
for(int i=0;i<4;++i)
if(fa->son[i]==this)
return i;
assert(0);
}
bool isRoot(int type) {return type?(!fa->imag || !imag):(fa->son[0]!=this && fa->son[1]!=this);}
Node()
{
fill(son,son+4,fa=0);
se=re=ch=all=nData;
mch=mre=nMark;
rev=imag=0;
}
void Link(int d,Node *s)
{
son[d]=s;
s->fa=this;
}
void Sets(Node *s,int d)
{
Down();
son[d]=s;
s->fa=this;
Up();
}
void Rev();
void Edit(Mark,int);
void Up();
void Down();
}*POOL=(Node*)malloc(SIZE*sizeof(Node)),*ME=POOL,**BIN=(Node**)malloc(SIZE*sizeof(Node*)),*null=new(ME++) Node,*node[MAXN];
int root;
Node *newNode(int val=INF)
{
Node *cur=*BIN?*BIN--:ME++;
fill(cur->son,cur->son+4,cur->fa=null);
cur->re=nData;
cur->mch=cur->mre=nMark;
cur->rev=0;
if(val==INF)
{
cur->se=cur->all=cur->ch=nData;
cur->imag=1;
}
else
{
cur->se=cur->all=cur->ch=Data(val,val,val,1);
cur->imag=0;
}
return cur;
}
void delNode(Node *&pos)
{
*++BIN=pos;
pos=0;
}
void Node::Rev()
{
if(this==null) return;
swap(son[0],son[1]);
rev^=1;
}
const int E_ALL=0,E_CH=1,E_RE=2;
void Node::Edit(Mark f,int type)
{
if(this==null) return;
if(type==E_ALL || type==E_CH)
{
se+=f;
mch+=f;
ch+=f;
}
if(type==E_ALL || type==E_RE)
{
mre+=f;
re+=f;
}
all=ch+re;
}
void Node::Up()
{
ch=son[0]->ch+son[1]->ch+se;
re=son[0]->re+son[1]->re+son[2]->all+son[3]->all;
all=ch+re;
}
void Node::Down()
{
if(rev)
{
son[0]->Rev();
son[1]->Rev();
rev=0;
}
if(mre!=nMark)
{
son[0]->Edit(mre,E_RE);
son[1]->Edit(mre,E_RE);
son[2]->Edit(mre,E_ALL);
son[3]->Edit(mre,E_ALL);
mre=nMark;
}
if(mch!=nMark)
{
son[0]->Edit(mch,E_CH);
son[1]->Edit(mch,E_CH);
mch=nMark;
}

}
void Trans(Node *pos)
{
Node *fa=pos->fa,*grf=fa->fa;
fa->Down(),pos->Down();
int d=pos->kind();
if(grf!=null)
grf->son[fa->kind()]=pos;
pos->fa=grf;
fa->Link(d,pos->son[d^1]);
pos->Link(d^1,fa);
fa->Up();
}
const int S_LCT=0,S_AAA=2;
void Splay(Node *pos,int type)
{
while(!pos->isRoot(type))
{
Node *fa=pos->fa;
if(!fa->isRoot(type))
Trans(fa->kind()==pos->kind()?fa:pos);
Trans(pos);
}
pos->Up();
}
void AddVirt(Node *pos,Node *fa)
{
if(pos==null) return;
fa->Down();
for(int i=2;i<4;++i)
if(fa->son[i]==null)
{
fa->Sets(pos,i);
return;
}
while(fa->son[2]->imag)
fa=fa->son[2];
Splay(fa,S_AAA);
Node *vi=newNode();
vi->Link(2,fa->son[2]);
vi->Link(3,pos);
fa->Sets(vi,2);
Splay(vi,S_AAA);
}
void DelVirt(Node *pos)
{
if(pos==null) return;
static Node **top=(Node**)malloc(SIZE*sizeof(Node*));
for(Node *p=pos->fa;p->imag;p=p->fa) *++top=p;
if(*top) (*top)->fa->Down();
for(;*top;top--) (*top)->Down();
Node *fa=pos->fa;
int d=pos->kind();
if(fa->imag)
{
fa->fa->Sets(fa->son[d^1],fa->kind());
Splay(fa->fa,S_AAA);
delNode(fa);
}
else
{
fa->Sets(null,d);
Splay(fa,S_AAA);
}
pos->fa=null;
}
void Access(Node *pos)
{
Node *pred=null;
while(pos!=null)
{
Splay(pos,S_LCT);
AddVirt(pos->son[1],pos);
DelVirt(pred);
pos->Sets(pred,1);
pred=pos;
for(pos=pos->fa;pos->imag;pos=pos->fa);
}
}
void Touch(Node *pos)
{
Access(pos);
Splay(pos,S_LCT);
}
void BeRoot(Node *pos)
{
Touch(pos);
pos->Rev();
}
void Link(Node *p1,Node *p2)
{
BeRoot(p1);
Touch(p2);
AddVirt(p1,p2);
}
void Cut(Node *p1,Node *p2)
{
BeRoot(p1);
Touch(p2);
p2->Sets(null,0);
p1->fa=null;
}
Node *FindRoot(Node *pos)
{
Touch(pos);
while(pos->son[0]!=null)
{
pos->Down();
pos=pos->son[0];
}
Splay(pos,S_LCT);
return pos;
}
Node *FindFa(Node *pos)
{
Touch(pos);
pos=pos->son[0];
if(pos==null) return 0;
while(pos->son[1]!=null)
{
pos->Down();//Down!!!!!!!
pos=pos->son[1];
}
Splay(pos,S_LCT);
return pos;
}
void ChangeFather(int pos,int father)
{
if(pos==root) return;
Node *fa=FindFa(node[pos]);
Cut(fa,node[pos]);
if(FindRoot(node[pos])!=FindRoot(node[father]))
Link(node[pos],node[father]);
else
Link(node[pos],fa);
}
namespace Chain
{
Data Query(int p1,int p2)
{
BeRoot(node[p1]);
Touch(node[p2]);
return node[p2]->ch;
}
void Edit(int p1,int p2,Mark f)
{
BeRoot(node[p1]);
Touch(node[p2]);
node[p2]->Edit(f,E_CH);
}
}
namespace Sub
{
Data Query(int rt)
{
Touch(node[rt]);
return node[rt]->se+node[rt]->son[2]->all+node[rt]->son[3]->all;
}
void Edit(int rt,Mark f)
{
Touch(node[rt]);
node[rt]->se+=f;
node[rt]->son[2]->Edit(f,E_ALL);
node[rt]->son[3]->Edit(f,E_ALL);
}
}
}using namespace AAA;
int main()
{
static int w[MAXN],edge[MAXN][2];
int n,m;get(n),get(m);
for(int i=1;i<=n-1;++i)
get(edge[i][0]),get(edge[i][1]);
for(int i=1;i<=n;++i)
{
get(w[i]);
node[i]=newNode(w[i]);
}
get(root);
for(int i=1;i<=n-1;++i) Link(node[edge[i][0]],node[edge[i][1]]);
for(int i=1;i<=m;++i)
{
int opt,x,y,z,Ans;get(opt);
Data res;
BeRoot(node[root]);
switch(opt)
{
case 0:
case 5:
get(x),get(y);
Sub::Edit(x,Mark(opt==0?0:1,y));
break;
case 1:
get(root);
break;
case 2:
case 6:
get(x),get(y),get(z);
Chain::Edit(x,y,Mark(opt==2?0:1,z));
break;
case 3://min
case 4://max
case 11://sum
get(x);
res=Sub::Query(x);
switch(opt)
{
case 3:Ans=res.minv;break;
case 4:Ans=res.maxv;break;
case 11:Ans=res.sumv;break;
}
put(Ans),putchar('\n');
break;
case 7://min
case 8://max
case 10://sum
get(x),get(y);
res=Chain::Query(x,y);
switch(opt)
{
case 7:Ans=res.minv;break;
case 8:Ans=res.maxv;break;
case 10:Ans=res.sumv;break;
}
put(Ans),putchar('\n');
break;
case 9:
get(x),get(y);
ChangeFather(x,y);
break;
}
}
return 0;
}

NOI 2015 寿司晚宴

发表于 2017-02-25 | 更新于 2018-06-18

Link

这个做法非常喵啊

Solution

也就是说两个集合里不能有相同的质因子 那就质因子分解,状压,似乎可以处理30以内的,总共10个质数的情况 可以发现,状压的很多维是没用的。具体说,对于每个质因子,只会出现[np][\dfrac np][​p​​n​​]次。ppp越大,越浪费。 此时可以想到O(n)O(\sqrt n)O(√​n​​​)分解质因数的做法:用n\sqrt n√​n​​​以内的数字试除,最后判断是否为1。也就是说,每个数字都至多只有一个≥n\ge \sqrt n≥√​n​​​的质因子。而在n\sqrt n√​n​​​范围内只有888个质数,那就利用这个性质搞一些事情吧。 好了。每个数字分解,记录≤n\le \sqrt n≤√​n​​​的八个质数的状态,再记录剩下的唯一一个大质数。 然后把大质数相同的一起DP。 f[S1][S2]f[S_1][S_2]f[S​1​​][S​2​​]表示相应状态的方案数目,g[0/1][S1][S2]g[0/1][S_1][S_2]g[0/1][S​1​​][S​2​​]表示这个大质数给了0/10/10/1(给了不一定要了)的方案数目。g[0/1]g[0/1]g[0/1]的转移是互相独立的。当处理完一组大质数相同的数的时候,((f[S1][S2])∗=−1)+=(g[0][S1][S2]+g[1][S1][S2])((f[S_1][S_2])*=-1)+=(g[0][S_1][S_2]+g[1][S_1][S_2])((f[S​1​​][S​2​​])∗=−1)+=(g[0][S​1​​][S​2​​]+g[1][S​1​​][S​2​​])

枚举SubSet

我不停地问自己:一定要写的这么诡异吗

1
#define subset(x,ALL) for(int __S__=(ALL),x=__S__,__flag__=0;!__flag__;x=(x-1)&__S__,__flag__=(x==__S__))

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define subset(x,ALL) for(int __S__=(ALL),x=__S__,__flag__=0;!__flag__;x=(x-1)&__S__,__flag__=(x==__S__))
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
using std::sort;
const int MAXN=500+11,pc=8,prime[]={2,3,5,7,11,13,17,19},S=(1<<8)-1,MAXS=S+11;
int P;
struct Number
{
int bigprime;
int other;
Number(){}
Number(int x)
{
for(int i=0;i<pc && x>=prime[i];++i)
if(x%prime[i]==0)
{
other|=(1<<i);
do x/=prime[i];
while(x%prime[i]==0);
}
bigprime=x;
}
bool operator <(const Number& a)const
{
if(bigprime!=a.bigprime)return bigprime<a.bigprime;
return other<a.other;
}
}num[MAXN];
int Solve(int n)
{
static ll f[MAXS][MAXS],g[2][MAXS][MAXS];
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int i=1;i<=n;++i)
new(num+i) Number(i);
sort(num+2,num+1+n);
f[0][0]=g[0][0][0]=g[1][0][0]=1;
for(int i=2;i<=n+1;++i)
{
if(num[i-1].bigprime==1 || num[i].bigprime!=num[i-1].bigprime)
{
for(int s1=0;s1<=S;++s1)
subset(s2,S&(~s1))
{
((f[s1][s2]*=-1)+=(g[0][s1][s2]+g[1][s1][s2]))%=P;
g[0][s1][s2]=g[1][s1][s2]=f[s1][s2];
}
}
if(i==n+1) break;
int cur=num[i].other;
for(int s1=S;~s1;--s1)
subset(s2,S&(~s1))
{
((g[0][s1|cur][s2])+=g[0][s1][s2])%=P;
((g[1][s1][s2|cur])+=g[1][s1][s2])%=P;
}
}
ll Ans=0;
for(int s1=0;s1<=S;++s1)
subset(s2,S&(~s1))
(Ans+=f[s1][s2])%=P;
(Ans+=P)%=P;
return Ans;
}
int main()
{
freopen("input","r",stdin);
int n;get(n),get(P);
put(Solve(n)),putchar('\n');
return 0;
}

NOI 2016 网格

发表于 2017-02-23 | 更新于 2018-06-18

卡常2h,依旧排名倒数

56分做法

对整张平面图求割点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
const int NM=1e6+11,MAXC=1e5+11,dx[]={1,-1,0,0},dy[]={0,0,1,-1};
int n,m,c;
struct point{int x,y;}p[MAXC];
inline int ID(int x,int y){return (x-1)*m+y;}
inline bool Inside(int x,int y){return 1<=x && x<=n && 1<=y && y<=m;}
bool isblock[NM];
namespace Graph
{
const int PS=NM,ES=PS<<2;
struct Edge
{
Edge *pre;
int to;
Edge(Edge *pre,int to):pre(pre),to(to){}
}*POOL=(Edge*)malloc(ES*sizeof(Edge)),*ME=POOL,*id[PS];
void Adde(int f,int t){id[f]=new(ME++) Edge(id[f],t);}
int dfn[PS],low[PS],dc,stack[PS],top,sc;
bool instack[PS],iscut[PS],hasCut;
void DFS(int pos,int fa)
{
instack[stack[++top]=pos]=1;
dfn[pos]=low[pos]=++dc;
int sonc=0;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(!dfn[u])
{
sonc++;
DFS(u,pos);
chkmn(low[pos],low[u]);
iscut[pos]|=(fa!=0 && low[u]>=dfn[pos]);
}
else if(instack[u])
chkmn(low[pos],dfn[u]);
}
iscut[pos]|=(fa==0 && sonc>1);
if(low[pos]==dfn[pos])
{
++sc;
while(stack[top+1]!=pos)
instack[stack[top--]]=0;
}
hasCut|=iscut[pos];
}
}using namespace Graph;
void Reset()
{
ME=POOL;memset(id,0,sizeof(id));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(instack,0,sizeof(instack));
memset(iscut,0,sizeof(iscut));
memset(isblock,0,sizeof(isblock));//..
hasCut=0;
dc=sc=0;
}
int Work()
{
get(n),get(m),get(c);
for(int i=1;i<=c;++i)
{
get(p[i].x),get(p[i].y);
isblock[ID(p[i].x,p[i].y)]=1;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(!isblock[ID(i,j)])
for(int d=0;d<4;++d)
{
int x=i+dx[d],y=j+dy[d];//i+dy[d]..
if(Inside(x,y) && !isblock[ID(x,y)])
Adde(ID(i,j),ID(x,y));
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
int id=ID(i,j);
if(!isblock[id] && !dfn[id])
DFS(id,0);
}
if(n*m-c<=1 || (n*m-c==2 && sc==1)) return -1;
if(sc!=1) return 0;
if(hasCut) return 1;
return 2;
}
int main()
{
int T;get(T);
while(T--)
Reset(),put(Work()),putchar('\n');
return 0;
}

84分做法

把相邻蛐蛐座标的间隔缩到3以内,求割点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
typedef long long ll;
const int NM=1e6+11,MAXC=1e5+11,dx[]={1,-1,0,0},dy[]={0,0,1,-1};
using std::sort;
using std::min;

int n,m,c;
struct point{int x,y;point(int x=0,int y=0):x(x),y(y){}}p[MAXC];
bool cmp_x(const point &a,const point &b){return a.x<b.x;}
bool cmp_y(const point &a,const point &b){return a.y<b.y;}
inline int ID(int x,int y){return (x-1)*m+y;}
inline bool Inside(int x,int y){return 1<=x && x<=n && 1<=y && y<=m;}
bool isblock[NM];
namespace Graph
{
const int PS=NM,ES=PS<<2;
struct Edge
{
Edge *pre;
int to;
Edge(Edge *pre,int to):pre(pre),to(to){}
}*POOL=(Edge*)malloc(ES*sizeof(Edge)),*ME=POOL,*id[PS];
void Adde(int f,int t){id[f]=new(ME++) Edge(id[f],t);}
int dfn[PS],low[PS],dc,stack[PS],top,sc;
bool instack[PS],iscut[PS],hasCut;
void DFS(int pos,int fa)
{
instack[stack[++top]=pos]=1;
dfn[pos]=low[pos]=++dc;
int sonc=0;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(!dfn[u])
{
sonc++;
DFS(u,pos);
chkmn(low[pos],low[u]);
iscut[pos]|=(fa!=0 && low[u]>=dfn[pos]);
}
else if(instack[u])
chkmn(low[pos],dfn[u]);
}
iscut[pos]|=(fa==0 && sonc>1);
if(low[pos]==dfn[pos])
{
++sc;
while(stack[top+1]!=pos)
instack[stack[top--]]=0;
}
hasCut|=iscut[pos];
}
}using namespace Graph;
void Reset()
{
Graph::ME=Graph::POOL;memset(id,0,sizeof(id));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(instack,0,sizeof(instack));
memset(iscut,0,sizeof(iscut));
memset(isblock,0,sizeof(isblock));//..
hasCut=0;
dc=sc=0;
}
const int D=3;
void Transform()
{
int delta;p[c+1]=point(n,m);
sort(p+1,p+1+c,cmp_x);
delta=0;
for(int i=1;i<=c+1;++i)
{
p[i].x-=delta;
if(p[i].x>p[i-1].x+D)
{
delta+=p[i].x-(p[i-1].x+D);
p[i].x=p[i-1].x+D;
}
}
sort(p+1,p+1+c,cmp_y);
delta=0;
for(int i=1;i<=c+1;++i)
{
p[i].y-=delta;
if(p[i].y>p[i-1].y+D)
{
delta+=p[i].y-(p[i-1].y+D);
p[i].y=p[i-1].y+D;
}
}
n=p[c+1].x,m=p[c+1].y;
}
int Work()
{
static int Case;
++Case;

get(n),get(m),get(c);
for(int i=1;i<=c;++i)
get(p[i].x),get(p[i].y);
ll diff=(ll)n*m-c;
Transform();
for(int i=1;i<=c;++i)
isblock[ID(p[i].x,p[i].y)]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(!isblock[ID(i,j)])
for(int d=0;d<4;++d)
{
int x=i+dx[d],y=j+dy[d];//i+dy[d]..
if(Inside(x,y) && !isblock[ID(x,y)])
Adde(ID(i,j),ID(x,y));
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
int id=ID(i,j);
if(!isblock[id] && !dfn[id])
DFS(id,0);
}
if(diff<=1 || (diff==2 && sc==1)) return -1;
if(sc!=1) return 0;
if(hasCut || n==1 || m==1) return 1;
return 2;
}
int main()
{
int T;get(T);
while(T--)
Reset(),put(Work()),putchar('\n');
return 0;
}

100分做法

从56到84,主要的思路是把图离散化。 84到100也是。 假设存在割点,那么一定是有障碍物围成了一个只有一个缺口的圈。所以只对与这个圈八联通的跳蚤建图求割点。 然后会发现,只处理与蛐蛐直接八联通的跳蚤会出现一些反例,那就每只蛐蛐扩两圈,处理24只跳蚤,就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
typedef long long ll;
using std::vector;
const int MAXC=1e5+11,PS=MAXC*25,//*24
dx[]={1,-1,0,0},dy[]={0,0,1,-1};
int n,m,c;
struct point{int x,y;point(){}point(int x,int y):x(x),y(y){}}p[MAXC];
#define HashCode(a) ((ll)(a.x-1)*m+a.y)
#define Inside(p) (1<=p.x && p.x<=n && 1<=p.y && p.y<=m)
bool isblock[PS];
point nodes[PS];int noc;
namespace _Hash_
{
const int SIZE=PS,P=2499997;
struct Node
{
Node *pre;
ll key;int val;
Node(Node *pre,ll key,int val):pre(pre),key(key),val(val){}
}*POOL=(Node*)malloc(SIZE*sizeof(Node)),*ME=POOL;
struct Hash
{
Node *id[P];
Node *Find(ll key)
{
register int hs=key%P;
for(register Node *p=id[hs];p;p=p->pre)
if(p->key==key)
return p;
return 0;
}
Node *Find(point p){return Find(HashCode(p));}
int &operator [](point p)
{
ll key=HashCode(p);
Node *ptr=Find(key);
if(!ptr)
{
int hs=key%P;
ptr=id[hs]=new(ME++) Node(id[hs],key,-1);
}
return ptr->val;
}
void Reset(){memset(id,0,sizeof(id));}
}ref;
}using namespace _Hash_;
bool BlockAround(point pos)
{
for(int mx=-1;mx<=1;++mx)
for(int my=-1;my<=1;++my)
{
point u(pos.x+mx,pos.y+my);
if(Inside(u) && ref.Find(u) && isblock[ref[u]]) return 1;
}
return 0;
}
namespace _Graph_
{
const int PS=::PS,ES=PS<<2;
struct Edge
{
Edge *pre;
int to;
Edge(Edge *pre,int to):pre(pre),to(to){}
}*POOL=(Edge*)malloc(ES*sizeof(Edge)),*ME=POOL,*id[PS];
void Adde(int f,int t) {id[f]=new(ME++) Edge(id[f],t);}
int dfn[PS],low[PS],dc,stack[PS],sn[PS],top,sc;
bool instack[PS],iscut[PS],hasCut;
void DFS(int pos,int fa)
{
instack[stack[++top]=pos]=1;
dfn[pos]=low[pos]=++dc;
int sonc=0;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(!dfn[u])
{
sonc++;
DFS(u,pos);
chkmn(low[pos],low[u]);
iscut[pos]|=(fa!=0 && low[u]>=dfn[pos]);
}
else if(instack[u])
chkmn(low[pos],dfn[u]);
}
iscut[pos]|=(fa==0 && sonc>1);
if(low[pos]==dfn[pos])
{
++sc;
while(stack[top+1]!=pos)
{
sn[stack[top]]=sc;
instack[stack[top--]]=0;
}
}
hasCut|=(iscut[pos] && BlockAround(nodes[pos]));
}
int bn[PS],bc=0;
void Maximal(point pos,int bnum)
{
bn[ref[pos]]=bnum;
for(int mx=-1;mx<=1;++mx)
for(int my=-1;my<=1;++my)
{
if(mx==0 && my==0) continue;
point u(pos.x+mx,pos.y+my);
if(Inside(u) && ref.Find(u) && !bn[ref[u]])
Maximal(u,bnum);
}
}
}using namespace _Graph_;
vector<int> fleas[MAXC];
void Reset()
{
memset(isblock,0,sizeof(isblock));
noc=0;
_Hash_::ME=_Hash_::POOL;
ref.Reset();
_Graph_::ME=_Graph_::POOL;
memset(id,0,sizeof(id));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
dc=0;
memset(sn,0,sizeof(sn));//?
sc=0;
memset(iscut,0,sizeof(iscut));
hasCut=0;
memset(bn,0,sizeof(bn));
for(int i=1;i<=bc;++i) fleas[i].clear();
bc=0;
memset(stack,0,sizeof(stack));//stack不清0
}

bool isCon()
{
for(int i=1;i<=c;++i)
if(!bn[i]) Maximal(p[i],++bc);
for(int i=1;i<=c;++i)
for(int mx=-2;mx<=2;++mx)
for(int my=-2;my<=2;++my)
{
if(mx==0 && my==0) continue;
point u(p[i].x+mx,p[i].y+my);
if(!Inside(u)) continue;
int &idx=ref[u];
if(idx==-1)
{
nodes[idx=++noc]=u;//p[i]..
fleas[bn[i]].push_back(idx);
}
}
for(int i=c+1;i<=noc;++i)
for(int d=0;d<4;++d)
{
point u(nodes[i].x+dx[d],nodes[i].y+dy[d]);
int idx;
if(Inside(u) && ref.Find(u) && !isblock[idx=ref[u]]) Adde(i,idx);
}
for(int i=c+1;i<=noc;++i)
if(!dfn[i])
DFS(i,0);
for(int i=1;i<=bc;++i)
{
int pred=0;
for(vector<int>::iterator it=fleas[i].begin();it!=fleas[i].end();++it)
if(!pred) pred=sn[*it];
else if(pred!=sn[*it]) return 0;
}
return 1;
}
int Work()
{
static int Case;
++Case;

get(n),get(m),get(c);
for(int i=1;i<=c;++i)
{
get(p[i].x),get(p[i].y);
nodes[ref[p[i]]=++noc]=p[i];
isblock[noc]=1;
}
ll diff=(ll)n*m-c;
bool Con=isCon();
if(diff<=1 || (diff==2 && Con)) return -1;
if(!Con) return 0;
if(hasCut || n==1 || m==1) return 1;
return 2;
}
int main()
{
freopen("input","r",stdin);
int T;get(T);
while(T--)
Reset(),put(Work()),putchar('\n');
return 0;
}

总的来看性价比最高的是写84分的离散化。

hdu 4372 Count the Buildings

发表于 2017-02-22 | 更新于 2018-06-18

Link

Solution

圆排列

对一个环形序列,在某个点固定一个数字,其它元素的任意排列,称为圆排列。可以看出,圆排列的方案数f(n)=n!n=(n−1)!f(n)=\dfrac {n!}n=(n-1)!f(n)=​n​​n!​​=(n−1)!

编号为nnn的最高,一定能看到 要在nnn的位置左侧放置一个长度恰为f−1f-1f−1的递增序列FFF,在右侧放置一个长度恰为b−1b-1b−1的递减序列BBB。 对于存在于序列FFF中的每个元素FiF_iF​i​​,FiF_iF​i​​与Fi+1F_{i+1}F​i+1​​之间的元素可以随便摆放,这就成了一个圆排列的形式。 再考虑到要分配固定长度的递增序列,也就相当于构造固定个数的圆排列,于是就转化为第一类 数。 答案就是Cf+b−2f−1Sn−1f+b−2C_{f+b-2}^{f-1}S_{n-1}^{f+b-2}C​f+b−2​f−1​​S​n−1​f+b−2​​

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put64(x) printf("%lld",x)
typedef long long ll;
const int N=2000,MAXN=N+11,P=1000000007;
struct Combine
{
ll C[MAXN][MAXN];
Combine()
{
C[0][0]=1;
for(int i=1;i<=N;++i)
{
C[i][0]=1;
for(int j=1;j<=i;++j)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}
}
ll operator ()(int n,int m){return n>=m?C[n][m]:0;}
}C;
struct Stir
{
ll S[MAXN][MAXN];
Stir()
{
S[0][0]=1;
for(int i=1;i<=N;++i)
{
S[i][0]=0;
for(int j=1;j<=i;++j)
S[i][j]=(S[i-1][j]*(i-1)%P+S[i-1][j-1])%P;
}
}
ll operator ()(int n,int k){return n>=k?S[n][k]:0;}
}S;
int main()
{
freopen("input","r",stdin);
int T;get(T);
while(T--)
{
int n,f,b;get(n),get(f),get(b);
ll Ans=f+b-2<=n?C(f+b-2,f-1)*S(n-1,f+b-2)%P:0;
put64(Ans),putchar('\n');
}
return 0;
}
/* AC Record(Bugs)

*/

BZOJ 1006 神奇的国度

发表于 2017-02-21 | 更新于 2018-06-18

Link

Solution

弦图的最小染色,很多结论都不会证明,所以题解待填

最精华的部分大概是线性维护最大值了。

一种最值问题的解法

%%jcvb%%

Problem

一个长度为nnn的序列,mmm次操作,每次可以选择删除一个数字,或者把一个数字的值增大111,要求实时维护最大数字的下标

Solution

显然可以用平衡树或者堆来做,但是相当大材小用,没有发挥问题的特性,复杂度也带着log\loglog

思路是:设数字的值域为[1,n][1,n][1,n],那就开nnn个桶,每个桶上iii挂着一个链表,表示值为iii的数字的下标。

修改aia_ia​i​​时直接把下标iii插入bucket[ai+1]bucket[a_i+1]bucket[a​i​​+1],记录一个值fff表示当前已知的最大值并更新。

删除通过bool[]实现。

查询需要从bucket[f]bucket[f]bucket[f]开始,删掉所有被删除了的数字,如果bucket[f]bucket[f]bucket[f]被删空了,就 ,直到找到一个没有被删除的数字即是答案。

复杂度:

每次操作至多增加一个链表节点,所以链表中共插入O(n+m)O(n+m)O(n+m)个节点,同样删除次数也是O(n+m)O(n+m)O(n+m)。

每次插入,fff要么+1+1+1,要么不变,所以fff的增加、减少次数都是O(m)O(m)O(m)。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//Code by Lucida
#include<bits/stdc++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}

const int MAXN=10000+11,MAXM=1000000+11;
struct Edge
{
int to,pre;
}e[MAXM<<1];int id[MAXN],ec;
void Adde(int f,int t)
{
e[++ec].to=t;e[ec].pre=id[f];id[f]=ec;
e[++ec].to=f;e[ec].pre=id[t];id[t]=ec;
}
namespace _List_
{
const int SIZE=MAXN+MAXM;
struct Node
{
Node *pre;
int val;
Node(Node *pre,int val):pre(pre),val(val){}
}*POOL=(Node*)malloc(SIZE*sizeof(Node)),*ME=POOL;
typedef Node List_Node;
struct List
{
typedef List_Node Node;
Node *head;
void push_front(int val){head=new(ME++) Node(head,val);}
void pop_front(){head=head->pre;}
}buck[MAXN];
}using namespace _List_;
int main()
{
freopen("input","r",stdin);
static int seq[MAXN],ud[MAXN],color[MAXN],pot[MAXN];
int n,m;get(n),get(m);
for(int i=1;i<=m;++i)
{
int f,t;get(f),get(t);
Adde(f,t);
}
int mx=0;
for(int i=1;i<=n;++i)
buck[pot[i]=0].push_front(i);
ud[0]=1;
for(int i=n;i>=1;--i)
{
int cur=-1;
do
{
if(cur==-1) cur=0;
else mx--;
while(buck[mx].head && ud[cur=buck[mx].head->val])
buck[mx].pop_front();
}while(ud[cur]);
ud[cur]=1;
seq[i]=cur;
for(int j=id[cur];j;j=e[j].pre)
{
int u=e[j].to;
if(!ud[u])
buck[++pot[u]].push_front(u);
chkmx(mx,pot[u]);
}
}
memset(ud,0,sizeof(ud));
int Ans=0;
for(int i=n;i>=1;--i)
{
int pos=seq[i];
for(int j=id[pos];j;j=e[j].pre)
ud[color[e[j].to]]=i;//e[j].to
for(color[pos]=1;ud[color[pos]]==i;++color[pos]);
chkmx(Ans,color[pos]);
}
put(Ans),putchar('\n');
return 0;
}

弦图的一些备忘

发表于 2017-02-21 | 更新于 2018-06-18

弦

连接环中不相邻的两点的边

弦图

任意长度>3>3>3的环都有一个弦

性质

  1. 弦图的每一个诱导子图是弦图
  2. n>3n>3n>3的诱导子图不是完全图

单纯点

N(v)N(v)N(v)表示与vvv相邻的点集,称vvv为单纯点当{v+N(v)}\{v+N(v)\}{v+N(v)}的诱导子图是一个团

性质

任何一个弦图至少有一个单纯点。 不是完全图的弦图至少有两个不相邻的单纯点。

完美消除序列

图中所有点的一个序列VVV满足∀vi∈V,vi\forall v_i\in V,v_i∀v​i​​∈V,v​i​​在vi,vi+1,...,vn{v_i,v_{i+1},...,v_n}v​i​​,v​i+1​​,...,v​n​​的诱导子图为一个单纯点。

性质

一个无向图是弦图当且仅当有一个完美消除序列。

最大势算法

从末尾向开头构造完美消除序列,定义节点的势为与之相邻的已经存在于完美消除序列的节点数目,每次选取势最大的点加入序列并更新相邻点的势,重复nnn次。 这个算法在不是弦图的时候也能正常运行求出结果,所以必须还要再判序列是否是完美序列才行

判断序列是否是完美消除序列

从后向前扫,在第iii个位置,检查seqiseq_iseq​i​​与在iii之后的相邻点是否成团。设在iii之后与seqiseq_iseq​i​​相邻的点集为VVV,那么只需要判断v1v_1v​1​​是否与V−v1V-v_1V−v​1​​中所有的点相邻即可。想不通为什么

Prufer编码的一些东西

发表于 2017-02-21 | 更新于 2018-06-18

prufer 基本上是转的->Dapper除了格式之外,写的不错

无约束条件

如果每个点的度数没有限制,那么一共有nnn个点,每个点可以出现在 数列中的任意位置, ,不同的prufer必定对应不同的树,由于 数列长度为n−2n-2n−2,那么不同的构成的树共有nn−2n^{n-2}n​n−2​​种。

所有点的度数有限制

如果点的度数有限制,则一个度数为xxx的节点在 数列中只会出现x−1x-1x−1次 如果这个节点是叶节点,即x==1x==1x==1,显然成立 否则,若度数为xxx,那么它被删边x−1x-1x−1次,即加入数列x−1x-1x−1次后成为叶节点,然后它就不会被再加入数列了 综上所述,结论成立

则设did_id​i​​表示第iii个节点的度数,只要求出 序列的总数就行了 Ans=Cn−2d1−1Cn−2−(d1−1)d2−1...Cdn−1dn−1Ans=C_{n-2}^{d_1-1}C_{n-2-(d_1-1)}^{d_2-1}...C_{d_n-1}^{d_n-1}Ans=C​n−2​d​1​​−1​​C​n−2−(d​1​​−1)​d​2​​−1​​...C​d​n​​−1​d​n​​−1​​ =(n−2)!∏(di−1)!=\dfrac {(n-2)!}{\prod(d_i-1)!}=​∏(d​i​​−1)!​​(n−2)!​​

混合的情况

BZOJ 1005 明明的烦恼 就是分别求出没有度数限制和有度数限制的情况,相乘即可,注意高精度运算。

1…111213…23
Lucida

Lucida

It's been a while.

222 日志
127 标签
© 2018 Lucida
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Muse v6.3.0