2020 Multi-University Training Contest 8 部分题解

A - Auto-correction

留坑。

 

B - Breaking Down News

dp[i]表示前i个数在合法划分情况下的最大值。

转移方程:dp[i] = max(dp[i] , dp[j] + [a[i] > a[j]] - [a[j] - a[i]]),其中i - R \leqslant j \leqslant i - La[i]是前缀和。

考虑权值线段树优化dp,分类讨论如下:

  • a[j] > a[i]dp[i] = max(dp[i] , dp[j] - 1)
  • a[j] < a[i]dp[i] = max(dp[i] , dp[j] + 1)
  • a[j] = a[i]dp[i] = max(dp[i] , dp[j])

对于i - R \leqslant j \leqslant i - L这个条件需要单调队列维护。

因为我常数太大了,需要手写deque,快读才能过。

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e6 + 10 ;
int mx[maxn << 2] ;
vector<int> v[maxn] ;
int l[maxn] , r[maxn] ;
int ls(int x)
{
   return x << 1 ;
}
int rs(int x)
{
   return x << 1 | 1 ;
}
void build(int id , int l , int r)
{
   if(l == r){mx[id] = -1e9 ; return ;}
   int mid = (l + r) >> 1 ;
   build(ls(id) , l , mid) ;
   build(rs(id) , mid + 1 , r) ;
   mx[id] = max(mx[ls(id)] , mx[rs(id)]) ;
}
void update(int id , int l , int r , int x , int y)
{
   int mid = (l + r) / 2 ;
   if(l == r && l == x){mx[id] = y ; return ;}
   if(x <= mid)  update(ls(id) , l , mid , x , y) ;
   else  update(rs(id) , mid + 1 , r , x , y) ;
   mx[id] = max(mx[ls(id)] , mx[rs(id)]) ;
}
int query(int id , int l , int r , int x , int y)
{
   int ans = -1e9 ;
   int mid = (l + r) / 2 ;
   if(y < x || y < l || x > r)  return -1e9 ; //不合法的询问
   if(x <= l && r <= y)  return mx[id] ;
   if(x <= mid)  ans = max(ans , query(ls(id) , l , mid , x , y)) ;
   if(y > mid)  ans = max(ans , query(rs(id) , mid + 1 , r , x , y)) ;
   return ans ;
}
template<typename T> void read(T &res)  //read(n) ;
{
    bool flag = 0 ;
    char ch ;
    while(!isdigit(ch = getchar()))(ch == '-') && (flag = 1) ;
    for(res = ch - 48 ; 
    isdigit(ch = getchar()) ; 
    res = (res << 1) + (res << 3) + ch - 48) ;
    flag && (res = -res) ;
}
int main()
{
    //std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    read(T) ;
    while(T --)
    {
        int n , L , R ;
        read(n) ;
        read(L) ;
        read(R) ;
        vector<int> a(n + 1 , 0) ;
        for(int i = 1 ; i <= n ; i ++)  read(a[i]) , a[i] += a[i - 1] ;
        int m = n * 2 + 1 ; //[-n , n] -> [1 , 2 * n + 1]
        build(1 , 1 , m) ;
        for(int i = 1 ; i <= m ; i ++)  v[i].clear() , l[i] = 0 , r[i] = -1 ;
        vector<int> dp(n + 1 , -1e9) ;
        dp[0] = 0 ;
        // i - R <= j <= i - L
        // a[j] > a[i]  dp[i] = max(dp[j] - 1)
        // a[j] < a[i]  dp[i] = max(dp[j] + 1)
        // a[j] = a[i]  dp[i] = max(dp[j])
        auto change = [&](int u)
        {
            return u + n + 1 ;
        } ;
        auto del = [&](int i)
        {
            if(i < 0 || dp[i] < -1e8)  return ;
            int t = change(a[i]) ;
            if(l[t] <= r[t] && v[t][l[t]] == i)  l[t] ++ ;
            if(l[t] > r[t])  update(1 , 1 , m , t , -1e9) ;
            else  update(1 , 1 , m , t , dp[v[t][l[t]]]) ;
        } ;
        auto add = [&](int i)
        {
            if(i < 0 || dp[i] < -1e8)  return ;
            int t = change(a[i]) ;
            while(l[t] <= r[t] && dp[v[t][r[t]]] <= dp[i])  r[t] -- ;
            if(r[t] + 1 <= (int)v[t].size() - 1)  r[t] ++ , v[t][r[t]] = i ;
            else  r[t] ++ , v[t].push_back(i) ;
            update(1 , 1 , m , t , dp[v[t][l[t]]]) ;
        } ;
        auto ask = [&](int i)
        {
            dp[i] = max(dp[i] , query(1 , 1 , m , 1 , change(a[i] - 1)) + 1) ;
            dp[i] = max(dp[i] , query(1 , 1 , m , change(a[i] + 1) , m) - 1) ;
            dp[i] = max(dp[i] , query(1 , 1 , m , change(a[i]) , change(a[i]))) ;
        } ;
        for(int i = 1 ; i <= n ; i ++)
        {
            del(i - R - 1) ;
            add(i - L) ;
            ask(i) ;
        }
        printf("%d\n" , dp[n]) ;
    }
    return 0 ;
}

 

C - Clockwise or Counterclockwise

温暖的签到题。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll cross(int x1, int y1, int x2, int y2, int x3, int y3)
{
    return 1ll*(x2-x1)*(y3-y1) - 1ll*(y2-y1)*(x3-x1);
}
void solve()
{
    int x1, y1, x2, y2, x3, y3;
    scanf("%d%d%d%d%d%d", &x1, &y1, &x2, &y2, &x3, &y3);
    if(cross(x1, y1, x2, y2, x3, y3)<0) puts("Clockwise");
    else puts("Counterclockwise");
}
int main()
{
    int _; scanf("%d", &_);
    while(_--) solve();
    return 0;
}

 

D - Discovery of Cycles

定义R[i]表示[i,R[i]]区间有环且R[i]是相对于i的最小值。

显然R[i]是递增的,可以双指针计算R

环可以通过LCT维护。

#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 5;
struct LCT
{
    int ch[N][2], fa[N], rev[N];
    int id[N], mn[N];
    void clear(int x)
    {
        ch[x][0] = ch[x][1] = fa[x] = rev[x] = 0;
        id[x] = mn[x] = N;
    }
    void init(int n) { for(int i=1; i<=n; i++) clear(i); }
    #define rs(x) (ch[fa[x]][1]==x)
    int notrt(int x) { return ch[fa[x]][0]==x || ch[fa[x]][1]==x; }
    void up(int x)
    {
        mn[x] = id[x];
        if(ch[x][0]) mn[x] = min(mn[x], mn[ch[x][0]]);
        if(ch[x][1]) mn[x] = min(mn[x], mn[ch[x][1]]);
    }
    void setrev(int x)
    {
        swap(ch[x][0], ch[x][1]);
        rev[x] ^= 1;
    }
    void down(int x)
    {
        if(rev[x])
        {
            if(ch[x][0]) setrev(ch[x][0]);
            if(ch[x][1]) setrev(ch[x][1]);
            rev[x] = 0;
        }
    }
    void downall(int x)
    {
        if(notrt(x)) downall(fa[x]);
        down(x);
    }
    void rotate(int x)
    {
        int y = fa[x], z = fa[y], k = rs(x), w = rs(y);
        if(notrt(y)) ch[z][w] = x;
        ch[y][k] = ch[x][!k]; fa[ch[x][!k]] = y;
        ch[x][!k] = y; fa[y] = x; fa[x] = z;
        up(y); up(x); up(z);
    }
    void splay(int x)
    {
        downall(x);
        for(int f=fa[x]; f=fa[x], notrt(x); rotate(x))
            if(notrt(f)) rotate(rs(x) == rs(f) ? f : x);
    }
    void access(int x)
    {
        for(int y=0; x; x=fa[y=x])
            splay(x), ch[x][1] = y, up(x);
    }
    void makeroot(int x)
    {
        access(x); splay(x);
        setrev(x);
    }
    int findroot(int x)
    {
        access(x); splay(x);
        while(ch[x][0]) down(x), x = ch[x][0];
        splay(x);
        return x;
    }
    void split(int x, int y)
    {
        makeroot(x);
        access(y); splay(y);
    }
    void link(int x, int y)
    {
        makeroot(x);
        fa[x] = y;
    }
    void cut(int x, int y)
    {
        split(x, y);
        fa[x] = ch[y][0] = 0;
        up(y);
    }
}T;
struct edge 
{ 
    int u, v; 
}e[N];
int n, m, q, pre[N];
int fa[N];
void init(int n) { iota(fa+1, fa+n+1, 1); }
int find(int x) { return x==fa[x] ? x : fa[x] = find(fa[x]); }
void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if(fx!=fy) fa[fx] = fy;
}
void solve()
{   
    scanf("%d%d%d", &n, &m, &q);
    T.init(n+m);
    init(n);
    for(int i=1; i<=m; i++)
    {
        T.mn[n+i] = T.id[n+i] = i;
        scanf("%d%d", &e[i].u, &e[i].v);
        if(find(e[i].u)!=find(e[i].v))
        {
            T.link(e[i].u, n+i); T.link(e[i].v, n+i);
            merge(e[i].u, e[i].v);
            pre[i] = 1;
        } 
        else
        {
            T.split(e[i].u, e[i].v);
            int id = T.mn[e[i].v];
            T.cut(e[id].u, e[id].v);
            pre[i] = id + 1;
            T.link(e[i].u, n+i); T.link(e[i].v, n+i);
        }
        pre[i] = max(pre[i], pre[i-1]);
    }
    int lst = 0;
    for(int i=1; i<=q; i++)
    {
        int l, r; scanf("%d%d", &l, &r);
        int k1 = (l^lst)%m + 1;
        int k2 = (r^lst)%m + 1;
        l = min(k1, k2), r = max(k1, k2);
        if(pre[r]>l) puts("Yes"), lst = 1;
        else puts("No"), lst = 0;
    }
}
int main()
{
    int _; scanf("%d", &_);
    while(_--) solve();
    return 0;
}

 

E - Easy NPC Problem

留坑。

 

F - Fluctuation Limit

温暖的签到题。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, k, l[N], r[N], x[N];
void solve()
{
    scanf("%d%d", &n, &k);
    for(int i=1; i<=n; i++) scanf("%d%d", l+i, r+i);
    for(int i=2; i<=n; i++)
    {
        int pl = l[i-1] - k, pr = r[i-1] + k;
        l[i] = max(l[i], pl);
        r[i] = min(r[i], pr);
        if(l[i]>r[i]) { puts("NO"); return; }
    }
    puts("YES");
    x[n] = l[n];
    for(int i=n-1; i>=1; i--)
    {
        int pl = max(x[i+1]-k, l[i]);
        x[i] = pl;
    }
    for(int i=1; i<=n; i++) printf("%d%c", x[i], " \n"[i==n]);
}
int main()
{
    int _; scanf("%d", &_);
    while(_--) solve();
    return 0;
}

 

G - Gaming of Co-prime Disallowance

迷一样的题意,不知道在说啥,只好自己脑补了另一个题意。

按照下面题意写代码才能过:

取过k张牌,如果k张牌的gcd=1,那么取第k张牌的人赢。
1.n个数的gcd>1,问你先手赢的概率
2.n个数的gcd=1,问你后手赢的概率

其实主要问题是怎么算一方赢的概率,不妨设为先手。

首先一个不超过10w的数最多有6个不同质因子。枚举第一张牌,对于剩下所有数的质因子要么是第一张牌中的质因子,要么不是,考虑状压枚举。

dp[i][j]表示选择了i张牌,且质因子状压是j的概率。通过这个dp可以算出答案。

考虑到精度的问题,需要边加边除。不然你直接除个n!直接凉凉。

具体转移看代码吧,细节有一丢丢麻烦。

#include<bits/stdc++.h>
using namespace std ;
typedef double db ;
const int maxn = 1e5 + 10 ;
const db eps = 1e-6 ;
int cnt = 0 ;
bool vis[maxn] ;
int prime[maxn] ;
void get_prime(int up) //??ɸ
{
    memset(vis , 0 , sizeof(vis)) ;
    vis[1] = 1 ;
    for(int i = 2 ; i <= up ; i ++)
    {
        if(!vis[i]) 
        prime[++ cnt] = i ;
        for(int j = 1 ; j <= cnt && i * prime[j] <= up ; j ++)
        {
            vis[i * prime[j]] = 1 ;
            if(i % prime[j] == 0) break ;
        }
    }
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    get_prime(100000) ;
    while(T --)
    {
        int n ;
        cin >> n ;
        vector<int> a(n + 1) ;
        for(int i = 1 ; i <= n ; i ++)  cin >> a[i] ;
        int d = a[1] ;
        for(int i = 2 ; i <= n ; i ++)  d = __gcd(d , a[i]) ;
        vector<vector<int>> p(n + 1) ;
        auto init = [&]()
        {
            for(int i = 1 ; i <= n ; i ++)
            {
                int tmp = a[i] ;
                for(int j = 1 ; prime[j] * prime[j] <= a[i] ; j ++)
                {
                    if(a[i] % prime[j] == 0)
                    {
                        p[i].push_back(prime[j]) ;
                        while(a[i] % prime[j] == 0)  a[i] /= prime[j] ;
                    }
                }
                if(a[i] > 1)  p[i].push_back(a[i]) ;
                a[i] = tmp ;
            }
        } ;
        init() ; 
        db ans = 0.0 ;
        auto cal = [&](int s)
        {
            if(a[s] == 1)  return 1.0 / n ;
            int num = (1 << p[s].size()) ;
            vector<vector<db>> dp(n + 1 , vector<db>(num , 0.0)) ;
            vector<int> c(num , 0) , d(num , 0) , e(num , 0) ;
            vector<vector<int>> f(num , vector<int>(num , 0)) ;
            db res = 0.0 ;
            for(int i = 1 ; i <= n ; i ++)
            {
                int t = 0 ;
                for(int j = 0 ; j < (int)p[s].size() ; j ++)
                {
                    if(a[i] % p[s][j] == 0)  t ^= (1 << j) ;
                }
                c[t] ++ ;
            }
            for(int i = 0 ; i < num ; i ++)  for(int j = 0 ; j < num ; j ++)
                if((i & j) == i)  d[i] += c[j] ;
            for(int i = 0 ; i < num ; i ++)  for(int j = 0 ; j < num ; j ++)
                if((i & j) == 0)  e[i] += c[j] ;
            for(int i = 0 ; i < num ; i ++)  for(int j = 0 ; j < num ; j ++)  for(int k = 0 ; k < num ; k ++)
                if((i & j) == k)  f[i][k] += c[j] ;
            dp[1][num - 1] = 1.0 / n ;
            for(int i = 1 ; i <= n - 1 ; i ++)
            {
                for(int j = 1 ; j < num ; j ++)
                {
                    for(int k = j ; k ; k = (k - 1) & j)
                    {
                        if(k == j && d[k] > i)  dp[i + 1][k] += dp[i][j] * (d[k] - i) / (n - i) ;
                        else if(k != j && f[j][k])  dp[i + 1][k] += dp[i][j] * f[j][k] / (n - i) ;
                    }
                    if((i + 1) % 2 == 1)  res += dp[i][j] * e[j] / (n - i) ;
                }
            }
            return res ;
        } ;
        if(d > 1)
        {
            if(n % 2 == 1)  cout << "1.000000000\n" ;
            else  cout << "0.000000000\n" ;
        }
        else
        {
            for(int i = 1 ; i <= n ; i ++)  ans += cal(i) ;
            cout << fixed << setprecision(9) << 1 - ans << '\n' ;
        }
        
    }
    return 0 ;
}

 

H - Hexagon

需要发现60^{\circ},60^{\circ},120^{\circ}交替出现的规律才会很好写。不过强力队友没发现此规律硬写三个小时也通过了。膜!

#include<bits/stdc++.h>
using namespace std;
#define M 1005
string ans;
int rx[]={0,1,1,0,-1,-1};
int ry[]={2,1,-1,-2,-1,1};
int mark[M<<1][M<<1];
int kase;
bool In(int x,int y,int n){
	return x<=n&&x>=-n&&x+y<=2*n&&x+y>=-2*n&&x-y<=2*n&&x-y>=-2*n;
}
bool check(int x,int y,int n){
	if(x==n||x==-n||x+y==2*n||x+y==-2*n||x-y==2*n||x-y==-2*n)return 1;
	return 0;
}
bool Near(int x,int y,int prex,int prey){
	for(int i=0;i<6;i++){
		if(x+rx[i]==prex&&y+ry[i]==prey)return 1;
	}
	return 0;
}
void move(int &x,int &y,int n){
	if(check(x,y,n)){
		for(int i=0;i<6;i++){
			int X=x+rx[i],Y=y+ry[i];
			if(In(X,Y,n)&&mark[X+M][Y+M]!=kase&&!check(X,Y,n)){
				ans+=(i+1+'0');
				x=X;y=Y;
				return;
			}
		}
		for(int i=0;i<6;i++){
			int X=x+rx[i],Y=y+ry[i];
			if(In(X,Y,n)&&mark[X+M][Y+M]!=kase&&check(X,Y,n)){
				ans+=(i+1+'0');
				x=X;y=Y;
				return;
			}
		}
	}
	else {
		for(int i=0;i<6;i++){
			int X=x+rx[i],Y=y+ry[i];
			if(In(X,Y,n)&&mark[X+M][Y+M]!=kase&&check(X,Y,n)){
				ans+=(i+1+'0');
				x=X;y=Y;
				return;
			}
		}
		for(int i=0;i<6;i++){
			int X=x+rx[i],Y=y+ry[i];
			if(In(X,Y,n)&&mark[X+M][Y+M]!=kase&&!check(X,Y,n)){
				ans+=(i+1+'0');
				x=X;y=Y;
				return;
			}
		}
	}
}
void solve(){
	int n;
	++kase;
	scanf("%d",&n);
	ans="";
	string tmp="612423534645156261";
	if(n&1){
		int x=0,y=0;
		mark[0+M][0+M]=kase;
		for(int i=3;i<=n;i+=2){
			ans+="61";
			mark[x-1+M][y+1+M]=kase;
			x--;y+=3;
			mark[x+M][y+M]=kase;
			int m=(i+i-3)*6;
			while(m--){
				move(x,y,i-1);
				mark[x+M][y+M]=kase;
			}
		}
		for(int i=0;i<18;i++)ans[i]=tmp[i]; 
	}
	else{
		ans+="134561";
		int x=-1,y=1;
		mark[0+M][0+M]=kase;
		mark[0+M][2+M]=kase;
		mark[1+M][1+M]=kase;
		mark[1+M][-1+M]=kase;
		mark[0+M][-2+M]=kase;
		mark[-1+M][-1+M]=kase;
		mark[-1+M][1+M]=kase;
		for(int i=4;i<=n;i+=2){
			ans+="61";
			mark[x-1+M][y+1+M]=kase;
			x--;y+=3;
			mark[x+M][y+M]=kase;
			int m=(i+i-3)*6;
			while(m--){
				move(x,y,i-1);
				mark[x+M][y+M]=kase;
			}
		}
	}
	cout<<ans<<'\n';
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--)solve(); 
	return 0;
} 

 

I - Isomorphic Strings

枚举k,对于s[1..k]k个循环同构串进行hash,然后枚举s[k+1..2*k],...,s[n-k+1..n]是否是s[1..k]的循环同构串。

手写hash可以通过。或者大力卡常。

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
using ull = uint64_t;
const ull base = 131, mod = 1e9 + 7;
const int N = 5e6 + 5;
const int M = 2174729;
struct HashSet
{
    struct node { int next; ull k; }e[M+5];
    int head[M], st[N], tp, sz;
    int f(ull k) { return k % M; }
    void clear() { sz = 0; while(tp) head[st[tp--]] = 0; }
    bool get(ull k) 
    {
        for(int p = head[f(k)]; p; p = e[p].next)
            if(e[p].k==k) return true;
        return false;
    }
    void ins(ull k)
    {
        if(get(k)) return;
        int t = f(k);
        e[++sz] = {head[t], k};
        head[t] = sz; st[++tp] = t;
    }
}st;
int n;
char s[N];
ull pw[N], h[N];
void init()
{
    pw[0] = 1;
    for(int i=1; i<=n; i++)
    {
        h[i] = (h[i-1]*base + s[i])%mod;
        pw[i] = pw[i-1]*base%mod;
    }
}
ull get(int l, int r) { return (h[r]-h[l-1]*pw[r-l+1]%mod+mod)%mod; }
bool check(int k)
{
    static ull ht[N];
    for(int i=1; i<=k; i++) ht[i] = (ht[i-1]*base + s[i])%mod;
    for(int i=1; i<=k; i++) ht[i+k] = (ht[i+k-1]*base + s[i])%mod;
    st.clear();
    for(int l=1; l<=k; l++)
    {
        int r = l + k - 1;
        ull cur = (ht[r]-ht[l-1]*pw[r-l+1]%mod+mod)%mod;
        st.ins(cur);
    }
    for(int l=1; l<=n; l+=k) if(!st.get(get(l, l+k-1))) return false;
    puts("Yes");
    return true;
}
void solve()
{
    scanf("%d%s", &n, s+1);
    init();
    if(n==1) { puts("No"); return; }
    bool same = 1;
    for(int i=2; i<=n; i++) same &= (s[i]==s[i-1]);
    if(same) { puts("Yes"); return; }
    for(int k=2; k*k<=n; k++)
    {
        if(n%k==0) 
        {
            if(check(k)) return;
            if(k*k!=n && check(n/k)) return; 
        }
    }
    puts("No");
}
int main()
{
    int _; scanf("%d", &_);
    while(_--) solve();
    return 0;
}

 

J - Jumping on a Cactus

留坑。

 

K - Kidnapper's Matching Problem

S构成的线性基是Bx消除B中的位后剩下x^{'}y消除B中的位后剩下y^{'}

结论是x\oplus y \in 2^S_{\oplus}\Leftrightarrow x^{'}=y^{'}

证明x^{'}=y^{'} \Rightarrow x\oplus y \in 2^S_{\oplus}x\oplus y = x^{'} \oplus y^{'} \oplus t,其中t可以被B表示。因为x^{'}=y^{'},则x\oplus y=t,即x\oplus y \in 2^S_{\oplus}

证明x\oplus y \in 2^S_{\oplus} \Rightarrow x^{'}=y^{'}:考虑反证,即证明x^{'}\neq y^{'} \Rightarrow x\oplus y \notin 2^S_{\oplus}x^{'},y^{'}都有B无法表示的位,因为x^{'}\neq y^{'},则x\oplus yB无法表示的位,即x\oplus y \notin 2^S_{\oplus}

然后把a,b在线性基中跑一遍后,再做一遍kmp即可。

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e5 + 10 ;
const int mod = 1e9 + 7 ;
struct Linear_base
{
    bool zero ;
    int cnt ;
    int b[35] , p[35] ;
    void init()
    {
        zero = 0 ;
        cnt = 0 ;
        memset(b , 0 , sizeof(b)) ;
        memset(p , 0 , sizeof(p)) ;
    }
    bool insert(int x)
    {
        for(int i = 29 ; i >= 0 ; i --)
        if(x & (1 << i))
        {
            if(b[i])  x ^= b[i] ;
            else  
            {
                b[i] = x ;
                return 1 ;
            }
        }
        zero = 1 ;
        return 0 ;
    }
    int get(int x)
    {
        for(int i = 29 ; i >= 0 ; i --)
            if(x & (1 << i))
            {
                if(b[i])  x ^= b[i] ;
            }
        return x ;
    }
} linear_base ;
int qpow(int a , int b) //快速幂
{
    if(b < 0)  return 0 ;
    int ans = 1 ; 
    a %= mod ;
    while(b)
    {
        if(b & 1)  ans = (1ll * ans * a) % mod ;
        b >>= 1 , a = (1ll * a * a) % mod ;
    }
    return ans % mod ;
}
struct KMP
{
    int s1[maxn] , s2[maxn] ;
    int len1 , len2 ;
    int nxt[maxn] ;
    void get_nxt(vector<int> a , vector<int> b)
    { 
        nxt[0] = -1 ;  
        int j = 0 , k = -1 ;
        len1 = a.size() - 1 ;
        len2 = b.size() - 1 ;
        for(int i = 0 ; i < len1 ; i ++)  s1[i] = a[i + 1] ;
        for(int i = 0 ; i < len2 ; i ++)  s2[i] = b[i + 1] ;
        while(j < len2)  
        {  
            if(k == -1 || s2[j] == s2[k])  j ++ , k ++ , nxt[j] = k ;  
            else  k = nxt[k] ;  
        } 
    } 
    void solve()
    {
        int ans = 0 ;
        int i = 0 , j = 0 ;
        while(i < len1)
        {
            if(j == -1 || s1[i] == s2[j])  i ++ , j ++ ;
            else  j = nxt[j] ;
            if(j == len2)  
            {
                ans += qpow(2 , i - len2) ;
                ans %= mod ;
                j = nxt[j] ;
            }
        }
        cout << ans << '\n' ;
    }
} kmp ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n , m , k ;
        cin >> n >> m >> k ;
        vector<int> a(n + 1) , b(m + 1) , c(k + 1) ;
        for(int i = 1 ; i <= n ; i ++)  cin >> a[i] ;
        for(int i = 1 ; i <= m ; i ++)  cin >> b[i] ;
        for(int i = 1 ; i <= k ; i ++)  cin >> c[i] ;
        linear_base.init() ;
        for(int i = 1 ; i <= k ; i ++)  linear_base.insert(c[i]) ;
        for(int i = 1 ; i <= n ; i ++)  a[i] = linear_base.get(a[i]) ;
        for(int i = 1 ; i <= m ; i ++)  b[i] = linear_base.get(b[i]) ;
        kmp.get_nxt(a , b) ;
        kmp.solve() ;
    }
    return 0 ;
}

 

L - Linuber File System

dp[i][j]表示节点i的所有祖先的修改的权值和是j的以i为根的子树的最小操作次数。

其中j只能是若干个l[i],r[i]0中的一个,如果不是,可以在不增加操作次数的前提下调整到是。具体原因只能脑补,不太会严格证明。有了这个结论就可以对l[i],r[i]离散化了。

如果j\in [l[i],r[i]]i节点可以选择不调整,也可以选择调整至[l[i],r[i]]范围内的其他权值。

如果j\notin [l[i],r[i]]i节点只能选择调整至[l[i],r[i]]范围内的其他权值。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int T ;
    cin >> T ;
    while(T --)
    {
        int n ;
        cin >> n ;
        vector<vector<int>> g(n + 1) ;
        vector<int> l(n + 1) , r(n + 1) ;
        vector<int> p ;
        for(int i = 1 ; i <= n - 1 ; i ++)
        {
            int u , v ;
            cin >> u >> v ;
            g[u].push_back(v) ;
            g[v].push_back(u) ;
        }
        for(int i = 1 ; i <= n ; i ++)  cin >> l[i] >> r[i] ;
        p.push_back(0) ;
        for(int i = 1 ; i <= n ; i ++)  p.push_back(l[i]) , p.push_back(r[i]) ;
        sort(p.begin() , p.end()) ;
        p.erase(unique(p.begin() , p.end()) , p.end()) ;
        int siz = p.size() ;
        vector<vector<int>> dp(n + 1 , vector<int>(siz , n)) ;
        for(int i = 1 ; i <= n ; i ++)
        {
            l[i] = lower_bound(p.begin() , p.end() , l[i]) - p.begin() ;
            r[i] = lower_bound(p.begin() , p.end() , r[i]) - p.begin() ;
        }
        //dp[i][j]表示节点i的所有祖先的修改的权值和是j的以i为根的子树的最小修改次数
        //j只能是若干个l[i],r[i]及0中的一个,如果不是,可以调整到是。
        function<void(int , int)> dfs = [&](int fa , int u)
        {
            vector<int> sum(siz , 0) ;
            vector<int> pre(siz , n) ;
            vector<int> suf(siz , n) ;
            for(auto v : g[u])
            {
                if(v == fa)  continue ;
                dfs(u , v) ;
                for(int i = 0 ; i < siz ; i ++)  sum[i] += dp[v][i] ;
            }
            for(int i = 0 ; i < siz ; i ++)
            {
                if(i == 0)
                {
                    if(l[u] <= i && i <= r[u])  pre[i] = sum[i] ;
                }
                else
                {
                    if(l[u] <= i && i <= r[u])  pre[i] = min(pre[i - 1] , sum[i]) ;
                    else  pre[i] = pre[i - 1] ;
                }
            }
            for(int i = siz - 1 ; i >= 0 ; i --)
            {
                if(i == siz - 1)
                {
                    if(l[u] <= i && i <= r[u])  suf[i] = sum[i] ;
                }
                else
                {
                    if(l[u] <= i && i <= r[u])  suf[i] = min(suf[i + 1] , sum[i]) ;
                    else  suf[i] = suf[i + 1] ;
                }
            }
            for(int i = 0 ; i < siz ; i ++)
            {
                if(l[u] <= i && i <= r[u])
                {
                    dp[u][i] = min(dp[u][i] , sum[i]) ;
                    int tmp1 = n ;
                    if(i > 0)  tmp1 = pre[i - 1] ;
                    int tmp2 = n ;
                    if(i + 1 < siz)  tmp2 = suf[i + 1] ;
                    dp[u][i] = min({dp[u][i] , tmp1 + 1 , tmp2 + 1}) ;
                }
                else
                {
                    int tmp1 = pre[r[u]] ;
                    int tmp2 = suf[l[u]] ;
                    dp[u][i] = min({dp[u][i] , tmp1 + 1 , tmp2 + 1}) ;
                }
            }
        } ;
        dfs(1 , 1) ;
        int zero = lower_bound(p.begin() , p.end() , 0) - p.begin() ;
        cout << dp[1][zero] << '\n' ;
    }
    return 0 ;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子。现在需要将所有的物品放入盒子,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子取出一个物品,放入物品数量最少的盒子。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值