「雅礼集训 2017」 【持续更新中】

目录

 

DAY1

T1 市场

T2 矩阵

T3 字符串

DAY2

T1 水箱

T2 棋盘游戏

T3 线段游戏

DAY4

T1 洗衣服

T2 编码

T3 猜数列

DAY 5

T1 远行

T2 珠宝

T3 矩阵

DAY7

T1 事情的相似度

T2 跳蚤王国的宰相

T3 蛐蛐国的修墙方案

DAY 8

T1 共

T2 价

T3 爷

DAY 10

T1 决斗

T2 数列

T3 拍苍蝇


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能过。。。?

先把哪些点被占领搞成格点的形式,然后和原图卷积就可以了。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值