目录
DAY1
T1 市场
线段树维护区间加减(加减值域1e4),区间下去整除法。
跟开方差不多,维护最大最小值,每次递归到所有数修改大小相等的时候变成区间加减。
复杂度:
首先我们考虑一段区间,若区间max - min有修改(会递归),这个max-min至少会除二(上取整)
然后每次区间加最多使得log个段(包括l和r的段)max-min不同,于是最多会有nlog个段每个段log次于是复杂度两个log
- 代码
#include<bits/stdc++.h>
using namespace std;
int n,q;
const int N=1e5+5;
typedef long long ll;
int mx[N<<2],tag[N<<2],mn[N<<2];ll sum[N<<2];
inline void pushdown(int x,int l,int r){
if(tag[x]!=0){
mx[x]+=tag[x],mn[x]+=tag[x];
sum[x]+=(ll)tag[x]*(r-l+1);
if(l<r)tag[x<<1]+=tag[x],tag[x<<1|1]+=tag[x];
tag[x]=0;
}
}
inline void Add(int x,int l,int r,int ql,int qr,int _sum){
int mid=(l+r)>>1;
pushdown(x,l,r);
if(ql<=l&&qr>=r){tag[x]+=_sum;return;}
if(ql<=mid)Add(x<<1,l,mid,ql,qr,_sum);
if(qr>mid)Add(x<<1|1,mid+1,r,ql,qr,_sum);
pushdown(x<<1,l,mid),pushdown(x<<1|1,mid+1,r);
sum[x] = sum[x<<1]+sum[x<<1|1];
mn[x] = min(mn[x<<1],mn[x<<1|1]);
mx[x] = max(mx[x<<1],mx[x<<1|1]);
}
int a[N];
inline void build(int x,int l,int r){
int mid=(l+r)>>1;
if(l==r){sum[x]=mx[x]=mn[x]=a[l];return ;}
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
sum[x] = sum[x<<1]+sum[x<<1|1];
mn[x] = min(mn[x<<1],mn[x<<1|1]);
mx[x] = max(mx[x<<1],mx[x<<1|1]);
}
inline void Div(int x,int l,int r,int ql,int qr,int _sum){
int mid=(l+r)>>1;
pushdown(x,l,r);
if(ql<=l&&qr>=r){
int nmx=mx[x]/_sum,nmn=mn[x]/_sum;
if(nmx*_sum>mx[x])nmx--;
if(nmn*_sum>mn[x])nmn--;
if(nmx-mx[x]==nmn-mn[x]){
tag[x]+=nmx-mx[x];
return ;
}else{
Div(x<<1,l,mid,ql,qr,_sum);
Div(x<<1|1,mid+1,r,ql,qr,_sum);
pushdown(x<<1,l,mid),pushdown(x<<1|1,mid+1,r);
sum[x] = sum[x<<1]+sum[x<<1|1];
mn[x] = min(mn[x<<1],mn[x<<1|1]);
mx[x] = max(mx[x<<1],mx[x<<1|1]);
}
return ;
}
if(ql<=mid)Div(x<<1,l,mid,ql,qr,_sum);
if(qr>mid)Div(x<<1|1,mid+1,r,ql,qr,_sum);
pushdown(x<<1,l,mid),pushdown(x<<1|1,mid+1,r);
sum[x] = sum[x<<1]+sum[x<<1|1];
mn[x] = min(mn[x<<1],mn[x<<1|1]);
mx[x] = max(mx[x<<1],mx[x<<1|1]);
}
inline ll qry(int x,int l,int r,int ql,int qr){
pushdown(x,l,r);
ll ans=0;int mid=(l+r)>>1;
if(ql<=l&&qr>=r)return sum[x];
if(ql<=mid)ans+=qry(x<<1,l,mid,ql,qr);
if(qr>mid)ans+=qry(x<<1|1,mid+1,r,ql,qr);
return ans;
}
inline int qrymin(int x,int l,int r,int ql,int qr){
pushdown(x,l,r);
int ans=0x3f3f3f3f;int mid=(l+r)>>1;
if(ql<=l&&qr>=r)return mn[x];
if(ql<=mid)ans=min(ans,qrymin(x<<1,l,mid,ql,qr));
if(qr>mid)ans=min(ans,qrymin(x<<1|1,mid+1,r,ql,qr));
return ans;
}
int main()
{
cin >> n >> q;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build(1,1,n);
while(q--){
int opt,l,r,c;
scanf("%d",&opt);
if(opt==1){
scanf("%d%d%d",&l,&r,&c);
++l,++r;
Add(1,1,n,l,r,c);
}
if(opt==2){
scanf("%d%d%d",&l,&r,&c);
++l,++r;
Div(1,1,n,l,r,c);
}
if(opt==3){
scanf("%d%d",&l,&r);
++l,++r;
printf("%d\n",qrymin(1,1,n,l,r));
}
if(opt==4){
scanf("%d%d",&l,&r);
++l,++r;
printf("%lld\n",qry(1,1,n,l,r));
}
// if(opt<=2){
// for(int i=1;i<=n;i++)cout << qry(1,1,n,i,i) << " ";puts("");
// }
}
}
T2 矩阵
贪心即可。
T3 字符串
根号分治,q<k的时候暴力询问,q>=k的时候暴力枚举子串然后看有多少个询问就行。
- 代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
struct SuffixAutomaton{
struct Node{
int ch[26],par,len;
int right;
}t[N];
int cnt,root,tail;
inline int newnode(int _l){
t[++cnt].len = _l;
return cnt;
}
inline void init(){
root = tail = newnode(0);
}
int anc[N][20];
inline void extend(int c){
int p = tail, np = newnode(t[tail].len + 1);
for(;p&&t[p].ch[c]==0;p=t[p].par) t[p].ch[c] = np;
if(!p) t[np].par = root;
else if(t[t[p].ch[c]].len == t[p].len + 1) t[np].par = t[p].ch[c];
else {
int q = t[p].ch[c], nq = newnode(t[p].len + 1);
memcpy(t[nq].ch,t[q].ch,sizeof(t[q].ch));
t[nq].par = t[q].par;
t[np].par = t[q].par = nq;
for(;p&&t[p].ch[c]==q;p=t[p].par)t[p].ch[c] = nq;
}
t[np].right = 1;
tail = np;
}
int buc[N],topo[N];
inline void toposet(){
int mx = 0;
for(int i=1;i<=cnt;i++)buc[t[i].len]++,mx = max(mx,t[i].len);
for(int i=1;i<=mx;i++)buc[i] += buc[i-1];
for(int i=1;i<=cnt;i++)topo[buc[t[i].len]--] = i;
}
inline void cal_right(){
toposet();
for(int i=cnt;i;i--){
t[t[topo[i]].par].right += t[topo[i]].right;
}
for(int i=1;i<=cnt;i++){
int x = topo[i];
anc[x][0] = t[x].par;
for(int i=1;1<<i<=cnt;i++)anc[x][i] = anc[anc[x][i-1]][i-1];
}
}
}SAM;
int n,m,q,k;
char s[N];
typedef pair<int,int> pii;
pii f[N];
int L[N],R[N];
char w[N];
vector<int> times[400][400];
int find(vector<int> &t,int ql,int qr){
if(t.size()==0)return 0;
// for(size_t i=0;i<t.size();i++)cout << t[i] << " ";
// puts("");
// cout << ql << " to " << qr << endl;
int ans = 0;
int l=-1,r=t.size()-1;
while(l<r){
int mid = (l+r+1)>>1;
if(t[mid] <= qr)l=mid;
else r=mid-1;
}
// cout << l << endl;
ans += l;
l=-1,r=t.size()-1;
while(l<r){
int mid = (l+r+1)>>1;
if(t[mid] <= ql - 1)l=mid;
else r=mid-1;
}
// cout << r << endl;
ans -= l;
// cout << ">> " <<ans << endl;
return ans;
}
int main()
{
cin >> n >> m >> q >> k;
scanf("%s",s+1);
SAM.init();
for(int i=1;i<=n;i++)SAM.extend(s[i]-'a');
SAM.cal_right();
for(int i=1;i<=m;i++){
scanf("%d%d",&L[i],&R[i]);
++L[i],++R[i];
if(q>=k){
times[L[i]][R[i]].push_back(i);
}
}
SAM.t[0].right = SAM.t[1].right = 0;
// cout << times[1][3].size() << endl;
for(int T=1;T<=q;T++){
// Term
int l,r;
if(q<k){
// cout << "plan A" << endl;
scanf("%s%d%d",w+1, &l,&r);
++l, ++r;
for(int i=l;i<=r;i++){
f[i] = pii(R[i],L[i]);
}
sort(f+l,f+r+1);
int cur = SAM.root;
int t = l;
long long ans = 0;
int curlen = 0;
for(int i=1;i<=k;i++){
int c = w[i]-'a';
for(int j=18;~j;j--)if(SAM.anc[cur][j])if(!SAM.t[SAM.anc[cur][j]].ch[c]){
cur = SAM.anc[cur][j];
}
if(!SAM.t[cur].ch[c])cur = SAM.anc[cur][0],curlen = SAM.t[cur].len;
if(!cur) cur = SAM.root,curlen = 0;
cur = SAM.t[cur].ch[c];if(cur)curlen ++ ;
// cout << cur << " " << SAM.t[cur].len << ' ' << SAM.t[cur].right << endl;
while(t<=r&&f[t].first == i){
int now = cur;
int len = f[t].first - f[t].second + 1;
// cout << "Q" << len << endl;
if(curlen < len){
++t;
continue;
}
for(int j=18;~j;j--)if(SAM.anc[now][j])if(SAM.t[SAM.anc[now][j]].len >= len){
now = SAM.anc[now][j];
}
// cout << len << "? " << now <<" "<< SAM.t[now].len << endl;
ans += SAM.t[now].right;
++t;
}
}
printf("%lld\n",ans);
}
else{
scanf("%s%d%d",w+1, &l,&r);
++l,++r;
long long ans = 0;
for(int i=1;i<=k;i++){
int cur = SAM.root;
for(int rr=i;rr<=k;rr++){
int c = w[rr]-'a';
cur = SAM.t[cur].ch[c];
ans += (long long)SAM.t[cur].right * find(times[i][rr],l,r);
}
}
printf("%lld\n",ans);
}
}
}
DAY2
T1 水箱
发现结构是一颗树,搞出来dp就行。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int h[N];
int par[N],fa[N];
typedef pair<int,int> pii;
pii ff[N];
int find(int x){
return par[x]==x?x:par[x] = find(par[x]);
}
int height[N];
int n,m;
int anc[N][20];
vector<pii> q[N];
int f[N][2];
int g[N][2];
int empty[N];
int main()
{
// freopen("A1.in","r",stdin);
int T;cin >> T;
while(T--) {
memset(anc,0,sizeof(anc));
memset(height,0,sizeof(height));
memset(fa,0,sizeof(fa));
cin >> n >> m;
for(int i=1;i<n;i++)scanf("%d",&h[i]),ff[i] = pii(h[i],i);
sort(ff+1,ff+n);
for(int i=1;i<=n*2+1;i++)par[i] = i;
int cnt = n;
for(int i=1;i<=n;i++)height[i] = 0;
for(int i=1;i<n;i++){
++cnt;
int t1 = find(ff[i].second),t2 = find(ff[i].second+1);
par[t1] = fa[t1] = par[t2] = fa[t2] = cnt;
height[cnt] = h[ff[i].second];
}
for(int i=cnt;i;i--){
anc[i][0] = fa[i];
for(int j=1;j<=18;j++)anc[i][j] = anc[anc[i][j-1]][j-1];
q[i].clear();
}
for(int i=1;i<=m;i++){
int id,y,k;scanf("%d%d%d",&id,&y,&k);
int tmp = id;
for(int j=18;~j;j--)if(anc[tmp][j] && height[anc[tmp][j]] <= y) tmp = anc[tmp][j];
//cout << tmp << height[tmp] << endl;
q[tmp].push_back(pii(y, k));
}
for(int i=1;i<=cnt;i++){
int l0 = 0,r1 = 0;
f[i][0] = f[i][1] = 0;
sort(q[i].begin(),q[i].end());
for(size_t j=0;j<q[i].size();j++){
if(!q[i][j].second)l0 ++ ;
}
f[i][0] = l0;
empty[i] = l0;
// 0 水位
for(size_t j=0;j<q[i].size();j++){
if(q[i][j].second)r1 ++ ;
else l0--;
if(j==q[i].size()-1||q[i][j].first!=q[i][j+1].first){
f[i][0] = max(f[i][0],l0+r1);
}
}
f[i][1] = l0 + r1;
g[i][0] = g[i][1] = 0;
}
int ans = 0;
for(int i=1;i<=cnt;i++){
int pp = fa[i];
g[i][0] = max(g[i][0] + empty[i], g[i][1] + f[i][0]);
g[i][1] = g[i][1] + f[i][1];
// cout << g[i][0] << " <> " << g[i][1] << endl;
ans = max(ans,g[i][0]);
g[pp][1] += g[i][1];
g[pp][0] += max(g[i][1],g[i][0]);
}
// puts("");
printf("%d\n",ans);
}
}
T2 棋盘游戏
二分图博弈板子。
T3 线段游戏
李超树板子。
DAY4
T1 洗衣服
前面的正着来有一个贪心,后面的倒着来有贪心,就行了。
T2 编码
这玩意是一个2-sat的模型
把所有串建出来搞一个trie,发现会向当前节点到根以及子树连边,于是可以优化2-sat的建边了。
需要注意可持久化一下以免自己连自己。
- 代码
#include<bits/stdc++.h>
using namespace std;
const int N = 8e6+5;
char chr[N];
vector<int> a[N];
int lenth[N];
int rk[N];
bool cmp(int a,int b){return lenth[a] > lenth[b];}
int sz,rt;
int id[N][2], ch[N][2];
int hed[N],to[N],nxt[N],cnt;
inline void adde(int u,int v){
if(!u||!v)return ;
to[++cnt] = v,nxt[cnt]=hed[u];hed[u]=cnt;
}
int stk[N],top;
int dfn[N],low[N],tim;
int col[N],ccnt;
inline void tarjan(int x){
//cout << x << endl;
dfn[x] = low[x] = ++tim;
stk[++top] = x;
for(int i=hed[x];i;i=nxt[i]){
int v=to[i];
if(!dfn[v]){
tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(!col[v]) low[x] = min(low[x],dfn[v]);
}
// cout << x << "!" ;
if(dfn[x] == low[x]) {
// cout << ccnt +1 << "!!";
++ccnt;
while(stk[top]!=x){
col[stk[top--]]=ccnt;
}col[stk[top--]]=ccnt;
}
}
void con(int x,int y){
if(!y)return;
adde(id[y][0],id[x][0]);
adde(id[x][1],id[y][1]);
}
int now,num;
inline void insert(int &d,int nw,int u,int v){
int p = d;d = ++num;id[d][0] = ++sz,id[d][1] = ++sz;
adde(id[p][0],id[d][0]);adde(id[d][1],id[p][1]);
ch[d][0] = ch[p][0],ch[d][1] = ch[p][1];
if(nw > lenth[now]){
con(d,ch[d][0]);con(d,ch[d][1]);
adde(u,id[p][1]),adde(id[p][0],v);
adde(u,id[d][0]),adde(id[d][1],v);
return ;
}
insert(ch[d][a[now][nw]],nw+1,u,v);
con(d,ch[d][0]);con(d,ch[d][1]);
}
int n;
int main()
{
cin >> n;
sz = n*2;
for(int i=1;i<=n;i++){
scanf("%s",chr+1);
int len = strlen(chr+1);
lenth[i] = len; rk[i] = i;
a[i].push_back(0);
for(int j=1;j<=len;j++)a[i].push_back(chr[j]=='0'?0:(chr[j]=='1'?1:-1));
}
sort(rk+1,rk+n+1,cmp);
for(int i=1;i<=n;i++){
now = rk[i];int pos = 0;
for(int j=1;j<=lenth[now];j++)if(a[now][j]==-1){pos=j;break;}
if(pos){
a[now][pos] = 0;insert(rt,1,i,i+n);
a[now][pos] = 1;insert(rt,1,i+n,i);
}else{
insert(rt,1,i,i+n);
adde(i+n,i);
}
}
// cout << "!" ;
for(int i=1;i<=sz;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++)if(col[i]==col[i+n]){
puts("NO");return 0;
}
puts("YES");
}
T3 猜数列
只填一个方向来维护转移。记一下当前往左、往右的分别是哪一个,然后转移是匹配和换线索,换线索要求是一个类似前缀的东西预处理出来就可以跑了。
代码有详细注释
- 代码
#include<bits/stdc++.h>
using namespace std;
const int N = 12;
const int INF = 0x3f3f3f3f;
int n,ans = INF, a[N][N], len[N], pre[N][N];
int dp[1<<N][N][N][N][N];
bool vis[1<<N][N][N][N][N],change[N][N][N];
int F(int S,int x,int p1,int y,int p2){
// y 向左 , x 向右, 分别匹配到p2,p1, 我们向左加字符
S &= (1<<n) - 1;
if(vis[S][x][p1][y][p2])return dp[S][x][p1][y][p2];
vis[S][x][p1][y][p2] = 1;
if(S==(1<<n)-1 && p1==0 && p2 == len[y]+1)
return dp[S][x][p1][y][p2] = 0;//已经结束
int ans = INF;
for(int i=1;i<=n;i++)if(!(S&(1<<(i-1))) && change[y][p2][i]){
ans = min(ans, F(S|(1<<(i-1)),x,p1,i,1));//换 y : --> i条开始,条件是直接加进去y也能满足
}
if(x&&p1==0){
for(int i=1;i<=n;i++)if(!(S&(1<<(i-1))))
for(int j=1;j<=len[i]+1;j++)if(change[i][j][x])
ans = min(ans, F(S|(1<<(i-1)),i,j-1,y,p2));//换x :--> i条j-1处 条件是x直接加到j后面能满足
}
if(a[x][p1]==a[y][p2] && a[x][p1])ans = min(ans,F(S,x,p1-1,y,p2+1)+1);
if(a[x][p1] && (p2&&pre[y][p2-1]&(1<<(a[x][p1]-1)))) ans = min(ans, F(S,x,p1-1,y,p2)+1);
if(a[y][p2] && (pre[x][p1]&(1<<(a[y][p2]-1)))) ans = min(ans, F(S,x,p1,y,p2+1)+1);
//加字符(匹配)
return dp[S][x][p1][y][p2] = ans;
}
bool pre_check(int x,int y,int z){
for(int i=1;i<=len[z];i++){
if(!(pre[x][y-1]&(1<<(a[z][i]-1)))){
if(y > len[x])return false;
if(a[x][y++] != a[z][i])return false;
}
}
return y > len[x];
}
int main()
{
cin >> n;
for(int i=1;i<=n;i++){
for(int j=1,c;;j++){
scanf("%d",&c);if(!c)break;
a[i][j] = c;len[i]++;
}
for(int j=1;j<=len[i];j++)pre[i][j] = pre[i][j-1] | (1<<(a[i][j] - 1));
}
for(int i=1;i<=n;i++){
change[0][1][i] = 1;
for(int j=1;j<=len[i]+1;j++)for(int k=1;k<=n;k++)if(i!=k)change[i][j][k] = pre_check(i,j,k);
}
pre[0][0] = -1;ans = INF;
for(int i=1;i<=n;i++)
ans = min(ans, F(1<<(i-1), i, len[i], 0, 1));
cout << (ans==INF?-1:ans) << endl;
}
DAY 5
T1 远行
一棵树,任一点到直径的距离一定是那个最大的距离,因此直径也可以合并了,然后lct跑dis合并一下就行了。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int u[N],v[N], par[N];
int find(int x){return par[x]==x?x:par[x]=find(par[x]);}
int Type,lastans;
struct Node{
int fa,ch[2],sz;
bool rev;
}t[N];
inline bool son(int x){return t[t[x].fa].ch[1]==x;}
inline bool isroot(int x){return t[t[x].fa].ch[1]!=x&&t[t[x].fa].ch[0]!=x;}
inline void pushdown(int x){
if(t[x].rev){
if(t[x].ch[0])t[t[x].ch[0]].rev^=1;
if(t[x].ch[1])t[t[x].ch[1]].rev^=1;
t[x].rev=0;
swap(t[x].ch[0],t[x].ch[1]);
}
}
inline void pushnow(int x){
// cout <<x<<": "<< t[x].fa << endl;
if(!isroot(x))pushnow(t[x].fa);
pushdown(x);
}
inline void pushup(int x){
t[x].sz=1;
if(t[x].ch[0])t[x].sz += t[t[x].ch[0]].sz;
if(t[x].ch[1])t[x].sz += t[t[x].ch[1]].sz;
}
inline void rotate(int x){
int f = t[x].fa, g = t[t[x].fa].fa;
bool a=son(x),b=son(x)^1;
if(!isroot(f))t[g].ch[son(f)]=x;
t[x].fa=g;
t[t[x].ch[b]].fa=f,t[f].ch[a]=t[x].ch[b];
t[x].ch[b]=f,t[f].fa=x;
pushup(f),pushup(x);
}
inline void splay(int x){
pushnow(x);
while(!isroot(x)){
int f=t[x].fa;
if(!isroot(f)){
if(son(x)^son(f))rotate(x);
else rotate(f);
}
rotate(x);
}
}
inline void access(int x){
int tmp=0;
while(x){
// cout << "a" << x << endl;
splay(x);
pushdown(x);
// cout << "b" << x << endl;
t[x].ch[1]=tmp;
//t[tmp].fa = x;
pushup(x);
tmp=x;x=t[x].fa;
}
}
inline void makeroot(int x){
//cout << x << "!";
// cout << t[4].fa << endl;
access(x);
//cout << "STP2" << endl;
splay(x);
t[x].rev^=1;
}
inline void link(int x,int y){
access(x);splay(x);
makeroot(y);
splay(y);
t[y].fa=x;
}
inline int dis(int a,int b){
//cout << a << " " << b << endl;
// cout << t[4].fa << endl;
makeroot(a);
//cout << "stp:1";
access(b);
//cout << "stp:2";
splay(b);
//cout << "stp:3\n";
return t[b].sz-1;
}
int n,m;
int main()
{
// freopen("hike1.in","r",stdin);
// freopen("my.out","w",stdout);
cin >> Type;
cin >> n >> m ;
for(int i=1;i<=n;i++)pushup(i),par[i]=i,u[i]=i,v[i]=i;
while(m--){
cerr<<m<<endl;
int opt,a,b;scanf("%d",&opt);
if(opt==1){
scanf("%d%d",&a,&b);
if(Type)a^=lastans,b^=lastans;
int x=find(a),y=find(b);
link(a,b);
par[x] = y;
int uu[4]={u[x],u[y],v[x],v[y]};
int vv[4]={u[x],u[y],v[x],v[y]};
int curdis=-1;
for(int i=0;i<=3;i++){
for(int j=0;j<=3;j++){
int d=dis(uu[i],vv[j]);
if(d>curdis)curdis=d,u[y]=uu[i],v[y]=vv[j];
}
}
}else{
scanf("%d",&a);
if(Type)a^=lastans;
// cout << a << " ";
int t = a;
a=find(a);
// cout << a << " ";
printf("%d\n",lastans=max(dis(t,u[a]),dis(t,v[a])));
}
}
}
T2 珠宝
之前写过,将就看看吧。。。
T3 矩阵
首先C一定在A的线性空间里。
然后对于秩是x的A,C的答案一共有2^(n-x)的n次方个
然后有一个性质是所有秩相同的C等价:一组A*B=C一定可以变成另外一组A*B=C(通过交换行列,线性变换什么的)
fij表示i*n的矩阵秩是j的方案,然后枚举A的秩x此时C的方案就是fx,r(r是C的rank)
除秩是r的C的个数就行了。
- 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
inline int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
inline int sub(int a,int b){a-=b;return a<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
/*math*/
const int N=2010;
int n;
bitset<N> vec[N];
int f[N][N];
int _t[N];
int main()
{
cin >> n;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
int t;scanf("%d",&t);
vec[i][j]=(bool)t;
}
_t[0]=1;
for(int i=1;i<=n;i++)_t[i]=mul(_t[i-1],2);
int r = 0;
for(int i=1;i<=n;i++){
int p = r+1;
for(;p<=n&&!vec[p][i];p++);if(p>n)continue;
++r;
swap(vec[p],vec[r]);
for(int j=r+1;j<=n;j++)if(vec[j][i])vec[j]^=vec[r];
}
f[0][0] = 1;
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++){
if(j)f[i][j]=mul(f[i-1][j-1],sub(_t[n],_t[j-1]));
f[i][j]=add(f[i][j],mul(f[i-1][j],_t[j]));
}
}
int ret=0;
for(int x=r;x<=n;x++){
ret=add(ret,mul(mul(f[n][x],f[x][r]),qpow(_t[n-x],n)));
}
printf("%d\n",mul(ret,qpow(f[n][r],mod-2)));
return 0;
}
DAY7
T1 事情的相似度
建一个SAM。
然后暴力是离线之后更新节点到根的max并且更新答案。
然后发现这个玩意就是依次access,lct就行。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5,SZ=2;
int n,m;
struct Node{
int ch[2],fa,sum,tag,lenth;
}t[N];
inline bool son(int x){return t[t[x].fa].ch[1]==x;}
inline bool isroot(int x){return t[t[x].fa].ch[1]!=x&&t[t[x].fa].ch[0]!=x;}
inline void pushdown(int x){
if(t[x].tag!=0){
t[x].sum = t[x].tag;
if(t[x].ch[1])t[t[x].ch[1]].tag = t[x].tag;
if(t[x].ch[0])t[t[x].ch[0]].tag = t[x].tag;
t[x].tag=0;
}
}
inline void pushnow(int x){
if(!isroot(x))pushnow(t[x].fa);
pushdown(x);
}
inline void rotate(int x){
int f=t[x].fa,g=t[t[x].fa].fa;
bool a=son(x),b=son(x)^1;
if(!isroot(f))t[g].ch[son(f)]=x;
t[x].fa=g;
t[t[x].ch[b]].fa=f,t[f].ch[a]=t[x].ch[b];
t[x].ch[b]=f,t[f].fa=x;
}
inline void splay(int x){
// cout << "s" << x << "--";
pushnow(x);
while(!isroot(x)){
// cout << "x" <<x <<"..";
int f=t[x].fa;
if(!isroot(f)){
if(son(x)^son(f))rotate(x);
else rotate(f);
}
rotate(x);
}
// cout << ":fin" << endl;
}
#define lowbit(x) ((x) & (-x))
int tt[N];
void upd(int x,int y){
for(;x;x-=lowbit(x)){
tt[x]=max(tt[x],y);
}
}
int qry(int x){
int ret=0;
for(;x<=n;x+=lowbit(x))ret=max(ret,tt[x]);
return ret;
}
inline void access(int x,int col){
int tmp=0;
while(x){
splay(x);pushdown(x);
t[x].ch[1]=tmp;
pushdown(x);
if(t[x].sum){
upd(t[x].sum,t[x].lenth);
}
tmp=x;
x=t[x].fa;
}
}
struct SuffixAutomaton{
struct Node{
int ch[2],par,len,right;
}t[N];
int root,tail,cnt;
int newnode(int len){t[++cnt].len=len;return cnt;}
inline void init(){
root=tail=newnode(0);
}
inline void extend(int c,int &id){
int p=tail,np=newnode(t[p].len+1);
for(;p&&!t[p].ch[c];p=t[p].par)t[p].ch[c]=np;
if(!p)t[np].par=root;
else if(t[t[p].ch[c]].len==t[p].len+1)t[np].par=t[p].ch[c];
else {
int q=t[p].ch[c],nq=newnode(t[p].len+1);
memcpy(t[nq].ch,t[q].ch,sizeof(t[q].ch));
t[nq].par=t[q].par;
t[q].par=t[np].par=nq;
for(;p&&t[p].ch[c]==q;p=t[p].par)t[p].ch[c]=nq;
}
t[np].right=1;
id=tail=np;
}
}SAM;
inline void build(){
// for(int i=1;i<=SAM.cnt;i++)cout << SAM.t[i].par << " ";puts("");
for(int i=1;i<=SAM.cnt;i++)t[i].fa=SAM.t[i].par,t[i].lenth=SAM.t[i].len;
}
char ch[N];
typedef pair<int,int> pii;
vector<pii> Q[N];
int ans[N],endd[N];
int main()
{
// freopen("1.in","r",stdin);
SAM.init();
cin >> n >> m;
scanf("%s",ch+1);
for(int i=1;i<=n;i++){
SAM.extend(ch[i]-'0',endd[i]);
}
build();
for(int i=1;i<=m;i++){
int l,r;scanf("%d%d",&l,&r);
Q[r].push_back(pii(l,i));
}
for(int i=1;i<=n;i++){
// cout << i <<" ";
access(endd[i],i);
splay(endd[i]);
t[endd[i]].tag=i;
// cout << "access!"<<endl;
for(size_t j=0;j<Q[i].size();j++){
ans[Q[i][j].second]=qry(Q[i][j].first);
}
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
T2 跳蚤王国的宰相
操作肯定是不断找大于n/2的接到根。每次找最大的一定更优。从重心往下跑就行了。
T3 蛐蛐国的修墙方案
搜索,把2的环特判掉就行了。这样是2^(n/4)*n的
DAY 8
T1 共
相当于n-k和k个的二分图,剩下的见bzoj4766
T2 价
左边表示不选,右边表示选,边权加上INF就可以控制个数相等了。
- 代码
#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=2e6+5;
typedef long long ll;
const ll INF = 1000000000000;
int n,m;
namespace graph{
int hed[N],cnt=1,to[M],nxt[M];ll cap[M],flow[M];
int S,T;
int d[N];bool vis[N];
void adde(int u,int v,ll _cap){
// cout << u << " " << v << " " << cap << endl;
++cnt;to[cnt]=v,cap[cnt]=_cap,nxt[cnt]=hed[u],flow[cnt]=0;hed[u]=cnt;
++cnt;to[cnt]=u,cap[cnt]=0,nxt[cnt]=hed[v],flow[cnt]=0;hed[v]=cnt;
}
queue<int>Q;
bool bfs(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
Q.push(S);vis[S]=1;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=hed[u];i;i=nxt[i]){
int v=to[i];
if(!vis[v]&&cap[i]>flow[i]){
vis[v]=1,d[v]=d[u]+1;
Q.push(v);
}
}
}
return vis[T];
}
ll dfs(int u,ll F){
if(u==T||!F)return F;
ll flownow=0;
for(int i=hed[u];i;i=nxt[i]){
int v=to[i];if(d[v]!=d[u]+1)continue;
ll f=dfs(v,min(F,cap[i]-flow[i]));
flownow+=f; F-=f;
flow[i]+=f,flow[i^1]-=f;
if(!F)break;
}
if(!flownow)d[u]=0;
return flownow;
}
ll max_flow(){
ll fw=0;
while(bfs()){
//cout << "A";
fw+=dfs(S,1000000000000000ll);
}
return fw;
}
}
using namespace graph;
int main(){
cin >> n;
S=n*2+1,T=n*2+2;
for(int i=1;i<=n;i++){
int t;scanf("%d",&t);
for(int j=1;j<=t;j++){
int v;scanf("%d",&v);
adde(i,n+v,INF);
}
}
ll ans=0;
for(int i=1;i<=n;i++){
int tmp;scanf("%d",&tmp);
adde(S,i,-tmp+INF);
ans += tmp-INF;
}
for(int i=1;i<=n;i++)adde(n+i,T,INF);
ll ret=max_flow();
// cout << ans << " " << ret << endl;
cout << ans+ret;
return 0;
}
T3 爷
分块的时候注意在max-min>=一个阈值的时候break就行,这样的复杂度是有保证的。(证明大概就是分开计算)
DAY 10
T1 决斗
可以找到一个位置使其无法穿过(算一下当前人数减空位数然后找最小的地方),之后就是个简单贪心了。
T2 数列
该怎么dp怎么dp就行了吧。
T3 拍苍蝇
好像bitset能过。。。?
先把哪些点被占领搞成格点的形式,然后和原图卷积就可以了。