多省联测
画风上来讲是最正常的一套省选题了,虽然我还是菜菜的。
签到题,轮廓线状压dp。然而愚蠢如我还是写挂了。
怕被卡常开链表hash存状态,大的hash值因为n最大到10应该用11进制,用了10进制然后GG。
sxy同学map+记忆化搜索踩爆我。
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
typedef long long LL;
typedef double db;
using namespace std;
const int N=4e6+7,bs=13,mod=1799797;
int n,m,tot,p[15],fir[mod+2],nxt[N];
LL hash[N],hash2[N],power[15],power2[15];
int A[15][15],B[15][15],f[2][N];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
void add() {
LL H=0,H2=0;
For(i,1,m) {
H=H*11+p[i];
H2=(H2*bs+p[i])%mod;
}
nxt[++tot]=fir[H2]; fir[H2]=tot; hash[tot]=H; hash2[tot]=H2;
}
void dfs(int pos) {
if(pos==m+1) {
add();
return ;
}
Rep(i,p[pos-1],0) {
p[pos]=i;
dfs(pos+1);
}
}
int get(LL H,int pos) {
return H/power[pos-1]%11;
}
int get_id(LL H,LL H2,int pos,int h) {
H=H+power[pos-1];
H2=(H2+power2[pos-1])%mod;
for(int i=fir[H2];i;i=nxt[i]) if(hash[i]==H)
return i;
}
#define DEBUG
int main() {
#ifdef DEBUG
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
#endif
memset(f[0],127,sizeof(f[0]));
memset(f[1],128,sizeof(f[1]));
read(n); read(m);
p[0]=n; dfs(1); power[0]=power2[0]=1;
For(i,1,10) {
power[i]=power[i-1]*11;
power2[i]=power2[i-1]*bs%mod;
}
For(i,1,n) For(j,1,m) read(A[i][j]);
For(i,1,n) For(j,1,m) read(B[i][j]);
f[0][1]=0; f[1][1]=0;//1:hei 0:bai
For(i,1,tot) {
LL H=hash[i],H2=hash2[i];
For(j,1,m) {
int h=get(H,j),hpr=get(H,j+1);
if(h==n||(j!=m&&h==hpr)) continue;
int id=get_id(H,H2,j,h);
f[0][i]=min(f[0][i],f[1][id]-B[h+1][m-j+1]);
f[1][i]=max(f[1][i],f[0][id]+A[h+1][m-j+1]);
}
}
printf("%d\n",f[1][tot]);
return 0;
}
太菜啦只会55分贪心,然后数据良心有60.
正解奥妙重重。
因为存在相同值,贪心显然是不对的,一个可以卡的数据是
考虑将数从大到小排序,选第一个数时,若它的子树大小为x,我们会选第x大的数作为它的值,然后1~x的值给它和它的子树。
然而若x右边还存在一些和x相等的数,把这些数中的一部分给x的儿子,就可以从x前面更大的数中选一些来给x的兄弟——x的兄弟的优先级一定比x的儿子高,结果也就更优了。
怎么办,emmmmm,我也不知道为什么就想到了这种奥妙的操作,我们考虑找到x右边最后一个和x相等的值的位置,在这个位置之前给x的子树预留sz那么多个数,然后再去考虑x的兄弟。
具体的做法是,用f[i]表i左边还可以选多少个数,f[i]=i-(i左边已经被预定的数)。
当找到一个最靠右的x,要在它左边预留sz个数的时候,把x右边包括x在内所有数的f值减去sz。
然后每次找当前可用最大值是在线段树上二分到一个右边所有f都大于sz的位置。
为什么这样是对的。考虑二分到一个位置x,x及右边所有f的最小值即为x及左边可用的位置数,因为所有预定区间中,完全包括在x左边的区间的值在x上已经减去了,预定在x右边的值又会在去min的时候限制到整段大区间,从而限制到x。
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=5e5+7;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 int n,d[N],dd[N],sta[N],v[N],sz[N],cnt[N],tot,top,fa[N],vis[N];
18 db k;
19
20 template<typename T> void read(T &x) {
21 char ch=getchar(); x=0; T f=1;
22 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
23 if(ch=='-') f=-1,ch=getchar();
24 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
25 }
26
27 bool cmp(const int &A,const int &B) {
28 return A>B;
29 }
30
31 int ecnt,fir[N],nxt[N<<1],to[N<<1];
32 void add(int u,int v) {
33 nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
34 }
35
36 int sg[N<<2],lz[N<<2];
37 #define lc x<<1
38 #define rc ((x<<1)|1)
39 #define mid ((l+r)>>1)
40 void down(int x,int l_len,int r_len) {
41 if(!lz[x]) return;
42 if(l_len) sg[lc]+=lz[x],lz[lc]+=lz[x];
43 if(r_len) sg[rc]+=lz[x],lz[rc]+=lz[x];
44 lz[x]=0;
45 }
46
47 void update(int x,int l,int r,int ql,int qr,int v) {
48 if(l>=ql&&r<=qr) {
49 sg[x]+=v; lz[x]+=v; return;
50 }
51 down(x,mid-l+1,r-mid);
52 if(ql<=mid) update(lc,l,mid,ql,qr,v);
53 if(qr>mid) update(rc,mid+1,r,ql,qr,v);
54 sg[x]=min(sg[lc],sg[rc]);
55 }
56
57 void build(int x,int l,int r) {
58 if(l==r) { sg[x]=cnt[l]; return; }
59 build(lc,l,mid); build(rc,mid+1,r);
60 sg[x]=min(sg[lc],sg[rc]);
61 }
62
63 int qry(int x,int l,int r,int mi) {
64 if(sg[x]>=mi) return l;
65 if(l==r) return 0;
66 down(x,mid-l+1,r-mid);
67 if(sg[rc]>=mi) {
68 int rs=qry(lc,l,mid,mi);
69 if(!rs) rs=mid+1; return rs;
70 }
71 else return qry(rc,mid+1,r,mi);
72 }
73
74 void dfs(int x) {
75 sz[x]=1;
76 for(int i=fir[x];i;i=nxt[i]) {
77 dfs(to[i]);
78 sz[x]+=sz[to[i]];
79 }
80 }
81
82 #define DEBUG
83 int main() {
84 #ifdef DEBUG
85 freopen("iiidx.in","r",stdin);
86 freopen("iiidx.out","w",stdout);
87 #endif
88 read(n); scanf("%lf",&k);
89 For(i,1,n) read(d[i]);
90 sort(d+1,d+n+1,cmp);
91 For(i,1,n) {
92 if(i==1||d[i]!=d[i-1]) tot++;
93 cnt[tot]++; dd[tot]=d[i];
94 }
95 For(i,2,tot) cnt[i]+=cnt[i-1];
96 build(1,1,tot);
97 Rep(i,n,1) {
98 db f=(db)i/k;
99 fa[i]=f;
100 if(fa[i]) add(fa[i],i);
101 else sta[++top]=i;
102 }
103 while(top) {
104 dfs(sta[top--]);
105 }
106 For(i,1,n) {
107 if(fa[i]&&!vis[fa[i]]) {
108 update(1,1,tot,v[fa[i]],tot,sz[fa[i]]-1);
109 vis[fa[i]]=1;
110 }
111 v[i]=qry(1,1,tot,sz[i]);
112 update(1,1,tot,v[i],tot,-sz[i]);
113 }
114 For(i,1,n-1) printf("%d ",dd[v[i]]);
115 printf("%d\n",dd[v[n]]);
116 return 0;
117 }
这里就是题解中说的学傻了在想点分治+FFT的人。
然后写了个NTT暴力,死活过不了样例,发现模数不能NTT(需满足p=2^k*奇数+1且2^k>2*p)
然后失去信仰打了个暴力树dp吊打标解。
标解似乎是什么拉格朗日+线段树合并优化背包。Orz orz%%%完全不会。
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=2149,mod=64123;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 LL f[1995][N],ans;
18 int v[N],tot,n,k,w,d[N];
19
20 template<typename T> void read(T &x) {
21 char ch=getchar(); x=0; T f=1;
22 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
23 if(ch=='-') f=-1,ch=getchar();
24 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
25 }
26
27 int ecnt,fir[N],nxt[N<<1],to[N<<2],sz[N];
28 void add(int x,int y) {
29 nxt[++ecnt]=fir[x]; fir[x]=ecnt; to[ecnt]=y;
30 nxt[++ecnt]=fir[y]; fir[y]=ecnt; to[ecnt]=x;
31 }
32
33 void dfs(int x,int fa) {
34 For(i,0,k) f[x][i]=0;
35 f[x][v[x]]=1; sz[x]=v[x];
36 for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
37 dfs(to[i],x);
38 Rep(j,sz[x],0) {
39 LL tp=f[x][j];
40 Rep(l,sz[to[i]],0) {
41 int y=min(j+l,k);
42 f[x][y]+=tp*f[to[i]][l]%mod;
43 if(f[x][y]>=mod) f[x][y]-=mod;
44 }
45 }
46 sz[x]+=sz[to[i]]; sz[x]=min(sz[x],k);
47 }
48 }
49
50 #define DEBUG
51 int main() {
52 #ifdef DEBUG
53 freopen("coat.in","r",stdin);
54 freopen("coat.out","w",stdout);
55 #endif
56 read(n); read(k); read(w);
57 For(i,1,n) read(d[i]);
58 For(i,2,n) {
59 int x,y;
60 read(x); read(y);
61 add(x,y);
62 }
63 For(W,1,w) {
64 tot=0;
65 For(i,1,n) { v[i]=(d[i]>=W); tot+=v[i]; }
66 if(tot<k) continue;
67 dfs(1,0);
68 For(i,1,n) {
69 ans+=f[i][k];
70 if(ans>=mod) ans-=mod;
71 }
72 }
73 printf("%lld\n",ans);
74 return 0;
75 }
匈牙利乱搞瞎暴力,然后就过了。
把匈牙利中每个导师的pr换成一个vector,只要还没满就继续往vector里塞。
每个人连出去的边也用vector存,对于匹配到的当前选手,用边集由志愿从小到大的顺序尝试匹配,之前匹配的选手就只能用它的答案的边集了。
第二问二分再暴力匹配,我直接用vector把第一问每个选手匹配前的pr状态复制下来了,然后二分后直接回到那个状态去重新暴力匹配。
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=207;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 int n,m,b[N],bb[N],s[N],a[N][N],ans[N][2],vis[N];
18 vector<int>vc[N][N];
19 vector<int>pr[N];
20 vector<int>tppr[N];
21 vector<int>P[N][N];
22
23 template<typename T> void read(T &x) {
24 char ch=getchar(); x=0; T f=1;
25 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
26 if(ch=='-') f=-1,ch=getchar();
27 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
28 }
29
30 int find(int x,int k) {
31 int up=vc[x][k].size();
32 For(i,0,up-1) {
33 int y=vc[x][k][i];
34 if(!vis[y]) {
35 vis[y]=1;
36 int upp=pr[y].size();
37 if(upp<b[y]) {
38 pr[y].push_back(x);
39 return 1;
40 }
41 else {
42 For(j,0,upp-1) {
43 int z=pr[y][j];
44 if(find(z,ans[z][0])) {
45 pr[y][j]=x;
46 return 1;
47 }
48 }
49 }
50 }
51 }
52 return 0;
53 }
54
55 int find2(int x,int k) {
56 int up=vc[x][k].size();
57 For(i,0,up-1) {
58 int y=vc[x][k][i];
59 if(!vis[y]) {
60 vis[y]=1;
61 int upp=tppr[y].size();
62 if(upp<b[y]) {
63 tppr[y].push_back(x);
64 return 1;
65 }
66 else {
67 For(j,0,upp-1) {
68 int z=tppr[y][j];
69 if(find2(z,ans[z][0])) {
70 tppr[y][j]=x;
71 return 1;
72 }
73 }
74 }
75 }
76 }
77 return 0;
78 }
79
80 int ck(int x,int pos) {
81 For(i,1,m) {
82 tppr[i]=P[pos][i];
83 //cerr<<tppr[i].size()<<endl;
84 }
85 int rs=0;
86 Rep(i,s[x],1) {
87 memset(vis,0,sizeof(vis));
88 rs=find2(x,i);
89 if(rs) break;
90 }
91 return rs;
92 }
93
94 #define DEBUG
95 int main() {
96 #ifdef DEBUG
97 freopen("mentor.in","r",stdin);
98 freopen("mentor.out","w",stdout);
99 #endif
100 int T,C;
101 read(T); read(C);
102 while(T--) {
103 read(n); read(m);
104 For(i,1,n) For(j,1,m) vc[i][j].clear();
105 For(i,1,m) pr[i].clear();
106 For(i,1,m) read(b[i]);
107 For(i,1,n) For(j,1,m) {
108 int x;
109 read(x);
110 a[i][j]=x;
111 if(x) vc[i][x].push_back(j);
112 }
113 For(i,1,n) read(s[i]);
114 For(i,1,n) {
115 For(j,1,m) {
116 P[i][j]=pr[j];
117 //cerr<<P[i][j].size()<<endl;
118 }
119 int rs=0;
120 For(j,1,m) {
121 if(!vc[i][j].empty()) {
122 memset(vis,0,sizeof(vis));
123 rs=find(i,j);
124 }
125 if(rs) { ans[i][0]=j; break;}
126 }
127 if(!rs) ans[i][0]=m+1;
128 }
129 For(i,1,n) {
130 int fl=0;
131 if(ans[i][0]<=s[i]) { ans[i][1]=0; continue; }
132 For(j,1,s[i]) if(!vc[i][j].empty()) { fl=1; break; }
133 if(!fl) ans[i][1]=i;
134 else {
135 int l=1,r=i-1,ok=0;
136 while(l<=r) {
137 int mid=((l+r)>>1);
138 if(ck(i,mid)) ok=mid,l=mid+1;
139 else r=mid-1;
140 }
141 ans[i][1]=i-ok;
142 }
143 }
144 For(i,1,n-1) printf("%d ",ans[i][0]);
145 printf("%d\n",ans[n][0]);
146 For(i,1,n-1) printf("%d ",ans[i][1]);
147 printf("%d\n",ans[n][1]);
148 }
149 return 0;
150 }
151 /*
152 3 5
153 2 2
154 1 1
155 2 2
156 1 2
157 1 1
158 2 2
159 1 1
160 1 2
161 1 2
162 2 1
163 2 2
164 1 1
165 0 1
166 0 1
167 2 2
168 */
60分做法,k很小。
题目等价于在树上选k条点不相交的链,使权值和最大。
f[x][k][0/1/2]表示在x的子树中选了k条链,x的度数为0,1,2的最优方案。
100分做法,发现答案对于k是凸包
然后二分斜率卡凸包
????我也不知道它在说什么
问了学长,还没得到回复
一道毒瘤数据结构码农题。
我写不出来。。。
太神啦 %%%%%