多省联测

 画风上来讲是最正常的一套省选题了,虽然我还是菜菜的。

d1t1一双木棋chess

签到题,轮廓线状压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
",f[1][tot]);
    return 0;
}
View Code

 

d1t2IIIDX

太菜啦只会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
",dd[v[n]]); 
116     return 0;
117 }
View Code

 

d1t3秘密袭击coat

这里就是题解中说的学傻了在想点分治+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
",ans);
74     return 0;
75 }
View Code

 

d2t1劈配

匈牙利乱搞瞎暴力,然后就过了。

把匈牙利中每个导师的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
",ans[n][0]);
146         For(i,1,n-1) printf("%d ",ans[i][1]);
147         printf("%d
",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 */ 
View Code

 

d2t2林克卡特树

60分做法,k很小。

题目等价于在树上选k条点不相交的链,使权值和最大。

f[x][k][0/1/2]表示在x的子树中选了k条链,x的度数为0,1,2的最优方案。

100分做法,发现答案对于k是凸包

然后二分斜率卡凸包

????我也不知道它在说什么

问了学长,还没得到回复

 

d2t3制胡窜

一道毒瘤数据结构码农题。

我写不出来。。。

 太神啦 %%%%%

 

 

 

原文地址:https://www.cnblogs.com/Achenchen/p/8869906.html