主頁 >  其他 > 2021湖南多校對抗賽第三場

2021湖南多校對抗賽第三場

2021-04-05 11:27:42 其他

2021湖南多校對抗賽第三場

排名

第一第二第三

團體成績

學校總題數總罰時

題解(部分)

special thanks: Binbin,驗了BDGL,出了兩道防AK題,太爹了%%%,

預測難度: K < L < G < F < C < E < I < A < B < D < J < H K\lt L \lt G \lt F \lt C \lt E \lt I \lt A \lt B \lt D \lt J \lt H K<L<G<F<C<E<I<A<B<D<J<H

實際難度: K < E < C < G < L < F < A < I < B D J H K\lt E \lt C \lt G \lt L \lt F \lt A \lt I \lt BDJH K<E<C<G<L<F<A<I<BDJH

幾道原創題沒人寫,sad

寫兩個好寫的非原創題題解+所有原創題題解,其它題題解可以在CF上找到(因為懶),

F

比較意外的F題,實際CF難度只有1700,

題意:給n個結點的圖,給出每個點的父結點,問最多修改幾條邊,讓它們形成一棵樹,

題解:實際上只需要干兩件事,如果圖中沒有環,那么一定是一個森林,只需要隨意指定一個樹的根,讓每一個樹的根都向那個根連邊就好了,如果有環其實差不多,只需要拆一條邊出來,往根結點連即可,并查集維護,

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int fa[maxn];
int f[maxn];
int n,rt;
int ans;
int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",fa+i);
        if(fa[i]==i) rt=i;
        f[i]=i;
    }
    for(int i=1;i<=n;i++) 
    {
        if(i==rt) continue;
        int u=find(i),v=find(fa[i]);
        if(u==v) //成環
        {
            if(rt==0) rt=i;
            f[u]=fa[i]=rt;
            ++ans;
        }
        else f[u]=v;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++) printf("%d ",fa[i]);
    return 0;
}

I

題意:給出一個有向圖,每一個點只有一個出邊,求一個最小的k,使得不論從哪個點u出發,走k步后到v,再從v走k步后還在v, 1 ≤ n ≤ 200 1 \le n \le 200 1n200

題解:找環,如果v在某一個環內,環的長度為|L|,那么k如果是|L|的倍數,走k步后一定會回到v,那么如果k是所有環長度的lcm,不管從哪個環的哪個點開始走k步,都會回到原點,此外,注意這個k還需要使得不管從哪個點開始走,走k步后一定在某一個環內,

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=5005;
const int mod=1e9+7;
int n;
int a[maxn],d[maxn];
bool vis[maxn],tag[maxn];
vector<int>sz;
vector<int>path;
void dfs(int u)
{
	if(vis[u]) return;
	path.push_back(u);
	vis[u]=1;
	dfs(a[u]);
}
void dfs2(int u,int &len) 
{
	if(tag[u]) return;
	dfs2(a[u],++len);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",a+i);
	for(int i=1;i<=n;i++) 
	{
		memset(vis,0,sizeof(vis));
		path.clear();
		dfs(i);
		if(a[path.back()]==i) //從i回到i,成環,
		{
			for(auto u:path) tag[u]=1;
			sz.push_back(path.size());
		}
	}
	ll ans=sz[0];
	for(int i=1;i<sz.size();i++) 
	{
		ll t=__gcd(ans,1ll*sz[i]);
		ans=ans/t*sz[i];
	}
	int mx=0;
	for(int i=1;i<=n;i++) 
	{
		int len=0;
		dfs2(i,len);
		mx=max(mx,len);
	}
	ll res=ans;
	while(res<mx) res+=ans;
	cout<<res<<endl;
	return 0;
}

B

題意:給一個01串,小球遇到0時能穿過,遇到1時被彈回,每次小球經過一個字符后會把字符反轉,給出Q個詢問,每次詢問給出 k i k_i ki?,問當一個個地放入 k i k_i ki?個小球時,最后01串的結果, 1 ≤ n ≤ 2 e 5 , 0 ≤ k ≤ 1 e 9 1\le n\le 2e5, 0\le k\le 1e9 1n2e5,0k1e9

題解:思維題,打個表觀察一下就會發現如下的性質:

  1. 如果最左邊的字符是1,小球被彈回,最左邊的字符變成0,
  2. 如果最左邊的字符是0,原來[2,n]的01串會全部取反后變為新串的[1,n-1]位置,新串的最后一個位置是1,

這樣一來,可以發現最多放入4個球,串的末尾就會多出一個01,那么最多放入2n個球,原串就會變為01交替的串,模擬這個程序即可,時間復雜度O(n),

#include<bits/stdc++.h>
#define ll long long
#define all(x) (x).begin(),(x).end()
#define P pair<int,int>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e6+5;
const int mod=998244353;
const int inv2=(mod+1)/2;
int a[maxn];
int n;
int fac[maxn];
int ans[maxn];
int main()
{
    fac[0]=1;
    for(int i=1;i<maxn;i++) fac[i]=fac[i-1]*2%mod;
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%1d",a+i);
    int sum=0,all=(fac[n-1]-1)*2%mod;
    for(int i=1;i<n;i++) if(a[i]) sum=(sum+fac[i])%mod;
    int p=0;bool rev=false;
    int tmp=n;
    for(int i=0;i<=2*n+1;i++) 
    {
        int now=rev^a[p];
        ans[i]=(sum+now)%mod;
        if(now==1) 
        {
            if(rev) a[p]=1;
            else a[p]=0;
        }
        else 
        {
            rev^=1;
            if(rev) a[tmp++]=0;
            else a[tmp++]=1;
            sum=(1ll*(all-sum)*inv2%mod+fac[n-1])%mod;
            sum=(sum-(a[++p]^rev)+mod)%mod;
        }
    }
    int Q,k;
    scanf("%d",&Q);
    while(Q--) 
    {
        scanf("%d",&k);
        k=min(k,2*n+(k&1));
        printf("%d\n",ans[k]);
    }
	return 0;
}

D

題意:給n個正整數,兩兩不同,要把這n個數分為兩個集合,使得集合A中任意兩數之差不小于x,集合B中任意兩數之差不小于y,問方案數, 1 ≤ n ≤ 1 e 5 , 1 ≤ a i , x , y ≤ 1 e 18 1\le n \le 1e5,1\le a_i,x,y \le 1e18 1n1e5,1ai?,x,y1e18

題解:DP,首先有一個O(n2)的DP比較顯然:

  1. 排序,用DPA(i,j)表示前i個數字中,第i個數字在集合A,集合B中最大的數字是a[j]的方案數,同理,DPB(i,j)表示前i個數字中,第i個數字在集合B,集合A中最大數字是a[j]的方案數,

  2. 轉移方程也很好列:

    考慮第i個數字放的位置,如果放在和i-1同一個集合:
    d p a [ i ] [ j ] + = d p a [ i ? 1 ] [ j ] ( a [ i ] ? a [ i ? 1 ] > = x ) d p b [ i ] [ j ] + = d p b [ i ? 1 ] [ j ] ( a [ i ] ? a [ i ? 1 ] > = y ) dpa[i][j]+=dpa[i-1][j]\ \ \ \ \ (a[i]-a[i-1]>=x) \\ dpb[i][j]+=dpb[i-1][j]\ \ \ \ \ (a[i]-a[i-1]>=y)\\ dpa[i][j]+=dpa[i?1][j] (a[i]?a[i?1]>=x)dpb[i][j]+=dpb[i?1][j] (a[i]?a[i?1]>=y)
    如果放在不同的集合:
    d p a [ i ] [ i ? 1 ] + = ∑ j = 0 i ? 1 d p b [ i ? 1 ] [ j ] ( a [ i ] ? a [ j ] > = x ) d p b [ i ] [ i ? 1 ] + = ∑ j = 0 i ? 1 d p a [ i ? 1 ] [ j ] ( a [ i ] ? a [ j ] > = y ) dpa[i][i-1]+=\sum_{j=0}^{i-1}dpb[i-1][j]\ \ \ \ \ (a[i]-a[j]>=x)\\ dpb[i][i-1]+=\sum_{j=0}^{i-1}dpa[i-1][j]\ \ \ \ \ (a[i]-a[j]>=y) dpa[i][i?1]+=j=0i?1?dpb[i?1][j] (a[i]?a[j]>=x)dpb[i][i?1]+=j=0i?1?dpa[i?1][j] (a[i]?a[j]>=y)
    那么答案就是 ∑ d p a [ n ] [ i ] + d p b [ n ] [ i ] \sum dpa[n][i]+dpb[n][i] dpa[n][i]+dpb[n][i],代碼如下:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=5005;
    const int mod=1e9+7;
    int n;
    ll x,y;
    ll a[maxn],dpa[maxn][maxn],dpb[maxn][maxn];
    int main()
    {
    	scanf("%d%lld%lld",&n,&x,&y);
    	for(int i=1;i<=n;i++) scanf("%lld",a+i);
    	sort(a+1,a+1+n);
    	a[0]=-1e18;
    	dpa[0][0]=1;
    	for(int i=1;i<=n;i++) 
    	{
    		for(int j=0;j<i;j++) 
    		{
    			if(a[i]-a[i-1]>=x) dpa[i][j]=(dpa[i][j]+dpa[i-1][j])%mod;
    			if(a[i]-a[i-1]>=y) dpb[i][j]=(dpb[i][j]+dpb[i-1][j])%mod;
    		}
    		ll sum=0;
    		ll sum2=0;
    		for(int j=0;j<i;j++) 
    		{
    			if(a[i]-a[j]>=x) sum=(sum+dpb[i-1][j])%mod;
    			if(a[i]-a[j]>=y) sum2=(sum2+dpa[i-1][j])%mod;
    		}
    		dpa[i][i-1]=(dpa[i][i-1]+sum)%mod;
    		dpb[i][i-1]=(dpb[i][i-1]+sum2)%mod;
    	}
    	ll ans=0;
    	for(int i=0;i<=n;i++) ans+=dpa[n][i]+dpb[n][i];
    	cout<<ans%mod<<endl;
    	return 0;
    }
    
  3. 考慮優化,空間上首先需要滾動陣列,那么第一個轉移方程就變成了一個區間賦值0的操作:對dpa,區間[j+1,i]變為0,其中j是滿足a[i]-a[j]>=x的最大j,同理對dpb,第二個轉移方程就變成了一個區間求和操作,這兩個操作都可以用線段樹維護,空間復雜度O(n),時間復雜度O(nlogn),代碼如下:

    #include<bits/stdc++.h>
    #define ll long long
    #define lson rt<<1
    #define rson rt<<1|1
    using namespace std;
    const int maxn=1e5+5;
    const int mod=1e9+7;
    int n;
    ll x,y;
    ll a[maxn];
    struct nod
    {
        ll val,tag;
        nod() {val=0;tag=-1;}
    }tra[maxn<<2],trb[maxn<<2];
    void up(int rt,nod *tr) {tr[rt].val=(tr[lson].val+tr[rson].val)%mod;}
    void down(int l,int r,int rt,nod *tr) 
    {
        if(tr[rt].tag!=-1) 
        {
            int m=l+r>>1;
            tr[lson].val=(m-l+1)*tr[rt].tag%mod;
            tr[rson].val=(r-m)*tr[rt].tag%mod;
            tr[lson].tag=tr[rson].tag=tr[rt].tag;
            tr[rt].tag=-1;
        }
    }
    void update(int l,int r,int rt,int L,int R,ll v,nod *tr) 
    {
        if(L<=l&&R>=r) {tr[rt].val=1ll*(r-l+1)*v%mod;tr[rt].tag=v;return;}
        down(l,r,rt,tr);
        int m=l+r>>1;
        if(L<=m) update(l,m,lson,L,R,v,tr);
        if(m<R) update(m+1,r,rson,L,R,v,tr);
        up(rt,tr);
    }
    ll qry(int l,int r,int rt,int L,int R,nod *tr) 
    {
        if(L<=l&&R>=r) return tr[rt].val;
        down(l,r,rt,tr);
        int m=l+r>>1;
        ll res=0;
        if(L<=m) res+=qry(l,m,lson,L,R,tr);
        if(m<R) res+=qry(m+1,r,rson,L,R,tr);
        up(rt,tr);
        return res%mod;
    } 
    int main()
    {
    	scanf("%d%lld%lld",&n,&x,&y);
    	for(int i=1;i<=n;i++) scanf("%lld",a+i);
    	sort(a+1,a+1+n);
    	a[0]=-1e18;
        update(1,n+1,1,1,1,1,tra);
    	for(int i=1;i<=n;i++) 
    	{
            int pa=upper_bound(a+1,a+1+n,a[i]-x)-a;
            int pb=upper_bound(a+1,a+1+n,a[i]-y)-a;
    
    		ll sum=qry(1,n+1,1,1,pa,trb);
    		ll sum2=qry(1,n+1,1,1,pb,tra);
            if(a[i]-a[i-1]<x) update(1,n+1,1,1,n+1,0,tra);
            if(a[i]-a[i-1]<y) update(1,n+1,1,1,n+1,0,trb);
            ll ax=qry(1,n+1,1,i,i,tra);
            ll bx=qry(1,n+1,1,i,i,trb);
            update(1,n+1,1,i,i,(ax+sum)%mod,tra);
            update(1,n+1,1,i,i,(bx+sum2)%mod,trb);
            
    	}
    	ll ans=tra[1].val+trb[1].val;
    	cout<<ans%mod<<endl;
    	return 0;
    }
    
  4. Binbin則直接發現了一個O(n)的解法(oh,請%斌姐姐),因為實際上洗掉操作對應的區間左端點是單調增的,所以不需要線段樹,只需要類似尺取法的方法暴力更新即可,

G

題意:你要給斌姐姐買女裝,女裝價值B元,但是你現在只有A元,有n件物品可以買,第i件物品價格 v i v_i vi?,每天能帶來 p i p_i pi?的收益,只能在第一天買物品,問至少需要幾天能湊夠B元, 1 ≤ n , A , v i , p i ≤ 2000 , 1 ≤ B ≤ 1 e 9 1 \le n,A,v_i,p_i\le 2000,1\le B \le 1e9 1n,A,vi?,pi?2000,1B1e9

題解:二分天數,假設需要x天,那么如果你買了物品i,x天后能帶來收益 x p i xp_i xpi?,之后就是一個01背包問題,直接DP就沒了,實際上可以不用二分,直接背包,

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2005;
int A,B;
int n;
int w[maxn];
ll v[maxn];
int b[maxn];
ll dp[maxn][maxn];
bool check(int x)
{
	for(int i=1;i<=n;++i) v[i]=1ll*b[i]*x;
    memset(dp,0,sizeof(dp));
	for(int i=1;i<=n;++i) 
	{
		for(int j=0;j<=A;++j) 
		{
            dp[i][j]=dp[i-1][j];
			if(j>=w[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i]);
		}
	}
    for(int i=0;i<=A;i++) if(dp[n][i]+(A-i)>=B) return true;
    return false;
}
int main()
{
	scanf("%d",&n);
	scanf("%d%d",&A,&B);
	for(int i=1;i<=n;i++) scanf("%d%d",w+i,b+i);
	int l=0,r=1e9,mid,ans=0;
	while(l<=r) 
	{
		mid=l+r>>1;
		if(check(mid)) 
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}

L

oh這題和昆明區域賽K撞的差不多了,實際上L是我們早就出好的,雷同純屬巧合,

題意:給一副13張牌的單花色麻將手牌,問聽牌,

題解:注意到雀頭只有一個,列舉雀頭是哪個,然后剩下的就是判斷面子,因為麻將牌每個數不會超過4張,所以對每一張牌,能夠構成刻子(3張相同)就成刻子,不能的話就構成順子,絕對是最優解,模擬,

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5005;
const int mod=1e9+7;
int n;
int cnt[20];
set<int>ans;
bool ok;
void dfs(int p)
{
	if(p==10) {ok=true;return;}
	if(cnt[p]>=3) 
	{
		cnt[p]-=3;
		dfs(p);
		cnt[p]+=3;
	}
	else 
	{
		if(cnt[p+1]>=cnt[p]&&cnt[p+2]>=cnt[p]) 
		{
			int tmp=cnt[p];
			cnt[p]-=tmp;
			cnt[p+1]-=tmp;
			cnt[p+2]-=tmp;
			dfs(p+1);
			cnt[p]+=tmp;
			cnt[p+1]+=tmp;
			cnt[p+2]+=tmp;
		}
	}
}
char s[20];
int main()
{
	int T;
	scanf("%d",&T);
	for(int cas=1;cas<=T;cas++)
	{
		scanf("%s",s);
		memset(cnt,0,sizeof(cnt));
		ans.clear();
		for(int i=0;s[i];i++) cnt[s[i]-'0']++;
		for(int i=1;i<10;i++) 
		{
			cnt[i]++;
			for(int j=1;j<10;j++) 
			{
				ok=false;
				if(cnt[j]>=2) 
				{
					cnt[j]-=2;
					dfs(1);
					cnt[j]+=2;
				}
				if(ok) break;
			}
			cnt[i]--;
			if(ok) ans.insert(i);
		}
		printf("Case #%d:\n",cas);
		cout<<ans.size()<<endl;
		for(auto x:ans) cout<<x<<' ';puts("");
	}
	return 0;
}

Update

H

因為被Ms. Ze釣魚搞的QQ封號,差點想把這題刪了…

考慮對于單個查詢,二分答案,要檢查一個答案x是否可行,那么問題就變成:

有x個容器,每個容器裝的球不可以相同,一個容器最多裝k個球,問是否可以把所有容器裝滿

對于出現次數 >=x的球,直接每個容器放一個,出現次數<x的球平鋪,則可以裝滿的條件是 s u m + s z ? x ≤ k ? x sum+sz*x\le k*x sum+sz?xk?x

s u m sum sum是所有 <= 的ai的和,sz是 >=ai的數字的個數

眾所周知主席樹可以處理出一個區間對應權值范圍的資訊,因為它帶修,所以需要樹狀陣列套主席樹,

在線段樹上二分來優化掉一個log,復雜度 n l o g 2 n nlog^2n nlog2n

資料結構部分比較套路,適合作為樹套樹入門題,

每當我寫樹套樹上二分的時候都會想起學弟的金玉良言:

在這里插入圖片描述

所以這題也可以用整體二分愉快的A掉,時間和空間都完爆樹套樹,
樹套樹做法:

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
#define all(vec) vec.begin(),vec.end()
using namespace std;
const int maxn = 1e5 + 5;
const ll N = 1e12;
int T[maxn];
int lc[maxn*500], rc[maxn*500], sz[maxn*500], tot = 0;
ll sum[maxn*500];
void update(int &rt, ll l, ll r, ll pos, int x){
    if(!rt) rt = ++tot, assert(tot < maxn*500);
    sz[rt]+=x; sum[rt] += (ll)x*pos;
    if(l==r) return;
    if(pos <= mid) update(lc[rt],l,mid,pos,x);
    else update(rc[rt],mid+1,r,pos,x);
    return;
}
int n, m, a[maxn];
void add_bit(int pos, ll val, int d){
    while(pos <= n){
        update(T[pos],1,N,val,d); pos += lowbit(pos);
    }
}
void sol(int x, int y, int k){
    vector<int> t1, t2; t1.clear(); t2.clear();
    vector<int> tmp;
    x--;
    ll l = 1, r = N;
    while(y) t1.pb(T[y]), y -= lowbit(y);
    while(x) t2.pb(T[x]), x -= lowbit(x);
    ll res_sum = 0, res_sz = 0;
    while(l < r){
        ll  lsum = 0, rsz = 0;
        for(int x:t1) lsum += sum[lc[x]], rsz += sz[rc[x]];
        for(int x:t2) lsum -= sum[lc[x]], rsz -= sz[rc[x]];
        ll sum = lsum+res_sum, sz = rsz+res_sz;
        ll lim = mid+1;
        if(sum+sz*lim >= lim*k){
            tmp.clear();
            for(int x:t1) if(rc[x]) tmp.pb(rc[x]);
            swap(tmp,t1);
            tmp.clear();
            for(int x:t2) if(rc[x]) tmp.pb(rc[x]);
            swap(tmp,t2); l = mid+1; res_sum = sum;
        }else{
            tmp.clear();
            for(int x:t1) if(lc[x]) tmp.pb(lc[x]);
            swap(tmp,t1);
            tmp.clear();
            for(int x:t2) if(lc[x]) tmp.pb(lc[x]);
            swap(tmp,t2); r = mid; res_sz = sz;
        }
    }
    printf("%lld\n", l);
}
void change(int x, int y){
    if(a[x] == y) return;
    add_bit(x, a[x], -1);
    add_bit(x, a[x]=y, 1);
}
int main()
{
    cin>>n>>m;
    fors(i,1,n+1){
        scanf("%d", &a[i]); add_bit(i, a[i], 1);
    }
    while(m--){
        int op; scanf("%d", &op);
        if(op == 1) {
            int l, r, k;
            scanf("%d%d%d", &l, &r, &k);
            sol(l, r, k);
        }
        else {
            int x, y; scanf("%d%d", &x, &y);
            change(x, y);
        }
    }
	return 0;
}

整體二分:

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
#define all(vec) vec.begin(),vec.end()
using namespace std;
const int maxn = 5e5 + 5;
const ll N = 1e12;
struct node{
    int op;//1,-1,0
    int l, r;//pos val
    ll sz, sum;
    int id, k;//

}e[maxn], q1[maxn], q2[maxn];
int n, m, tot=0;
ll sz[maxn], sum[maxn];
void add(ll a[], int i, ll x){
    while(i<=n) a[i] += x, i += lowbit(i);
}
ll qry(ll a[], int i){
    ll res=0; while(i) res+=a[i], i -= lowbit(i); return res;
}
ll ans[maxn];
void sol(ll l, ll r, int L, int R){
    if(l > r || L > R) return;
    int p1 = 0, p2 = 0;
    fors(i,L,R+1){
        if(e[i].op){
            if(e[i].r <= mid) add(sum, e[i].l, e[i].op*e[i].r),q1[p1++]=e[i];
            else add(sz, e[i].l, e[i].op), q2[p2++]=e[i];
        }else{
            ll csz = qry(sz, e[i].r)-qry(sz, e[i].l-1) + e[i].sz;
            ll csum = qry(sum, e[i].r)-qry(sum,e[i].l-1) + e[i].sum;
            if((ll)e[i].k*mid <= csum + csz*mid){
                ans[e[i].id] = mid;
                e[i].sum = csum;
                q2[p2++] = e[i];
            }else{
                e[i].sz = csz;
                q1[p1++] = e[i];
            }
        }
    }
    int o = L;
    fors(i,0,p1) {
        e[o++] = q1[i];
        if(q1[i].op) add(sum, q1[i].l, -q1[i].op*q1[i].r);
    }
    fors(i,0,p2) {
        e[o++] = q2[i];
        if(q2[i].op) add(sz, q2[i].l, -q2[i].op);
    }
    assert(qry(sum,n) == 0 && qry(sz, n) == 0);
    sol(l, mid-1, L, L+p1-1);
    sol(mid+1,r,L+p1,R);
    return;
}
int a[maxn];
int main()
{
    cin>>n>>m;
    fors(i,1,n+1){
        int x; scanf("%d", &x);a[i]=x;
        e[tot].op = 1; e[tot].l = i; e[tot].r = x; tot++;
    }
    fors(i,0,m){
        int op; scanf("%d", &op);
        if(op == 1){
            int l,r,k;
            scanf("%d%d%d", &l, &r, &k);
            e[tot].op = 0; e[tot].l=l; e[tot].r = r; e[tot].id = i; e[tot].k = k; tot++;
        }else{
            ans[i] = -10086;
            int x, y; scanf("%d%d", &x, &y);
            e[tot].op = -1; e[tot].l = x; e[tot].r = a[x]; tot++;
            e[tot].op = 1; e[tot].l = x; e[tot].r = (a[x]=y); tot++;
        }
    }
    sol(1, N, 0, tot-1);
    fors(i,0,m){
        if(ans[i]==-10086) continue;
        printf("%lld\n", ans[i]);
    }
	return 0;
}

J

好想看Ms. Rain穿JK啊

每次可以往上跳奇數格,有m個格子是不能跳的,問方案數

很容易有一個O(n)的dp:

d p ( i ) = ∑ d p ( j ) 其 中 ( j ? i ) % 2 = = 1 dp(i)=\sum dp(j) 其中\ (j-i)\%2==1 dp(i)=dp(j) (j?i)%2==1

當i上是障礙物則 d p ( i ) = 0 dp(i)=0 dp(i)=0,那么我們用 s ( i ) s(i) s(i)表示 ∑ j ≤ i d p ( j ) \sum_{j\le i} dp(j) ji?dp(j),其中 j 與 i 同 奇 偶 j與i同奇偶 ji,則有

d p ( i ) = s ( i ? 1 ) , s ( i ) = s ( i ? 2 ) + d p ( i ) dp(i)=s(i-1), s(i)=s(i-2)+dp(i) dp(i)=s(i?1),s(i)=s(i?2)+dp(i)

得到 s ( i ) = s ( i ? 1 ) + s ( i ? 2 ) s(i)=s(i-1)+s(i-2) s(i)=s(i?1)+s(i?2) i i i是可以跳的格子的情況)

i i i是不可以跳的格子的時候, s ( i ) = s ( i ? 2 ) s(i)=s(i-2) s(i)=s(i?2)

那么你可以發現,在一段連續的可以跳的區間, s s s的遞推式是可以用矩陣快速冪加速的,而遇到一個障礙物的時候,其實相當于交換遞推式第一項和第二項的值,再去進行下一輪的遞推,為了方便,我們可以設定 n + 1 n+1 n+1也是一個不可以跳的點,然后分段的進行矩陣快速冪,最后得到 s ( n ) s(n) s(n) s ( n ? 1 ) s(n-1) s(n?1), 其中 s ( n ? 1 ) s(n-1) s(n?1)即為答案(需要特判n不可以跳的情況)

至此,我們就可以欣賞雨姐姐穿JK的樣子了(

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int N = 2;
const int mod = 1e9 + 7;
struct mt{
    int a[N][N];
    mt(){memset(a,0,sizeof a);}
    void E(){fors(i,0,N) a[i][i] = 1;}
    mt operator * (mt A){
        mt ans;
        fors(i, 0, N) fors(k, 0, N) if(a[i][k]) fors(j, 0, N)
            ans.a[i][j] = (ans.a[i][j] + ((ll)a[i][k] * A.a[k][j])%mod )%mod;
        return ans;
    }
    void show(){
        fors(i, 0, N) {fors(j, 0, N) cout<<a[i][j]<<" "; cout<<endl;}
    }
};
mt qm(mt A, ll b){
    mt res; res.E();
    while(b){
        if(b&1) res = res*A;
        A = A*A;
        b >>= 1;
    }return res;
}
int solve(ll n, int m, vector<ll>& a) {
    assert(n > 0 && n <= 1e18);
    assert(m > 0 && m <= 1e5);
    assert(a.size() == m);
    for(ll x:a) assert(x <= n && x > 0);
    a.pb(n+1);
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(),a.end()), a.end());

    assert(a[0] > 0 && a[m-1] <= n);
    fors(i, 0, a.size()) if(a[i] == n) return 0;
    mt A, B;
    A.a[0][0] = A.a[0][1] = A.a[1][0] = 1;
    int f[2] = {1, 0};
    ll pre = 0;
    fors(i, 0, a.size()){
        ll num = a[i] - pre - 1;
        B = qm(A, num);
        int t0 = ((ll)B.a[0][0] * f[0]%mod + (ll)B.a[0][1] * f[1]%mod)%mod;
        int t1 = ((ll)B.a[1][0] * f[0]%mod + (ll)B.a[1][1] * f[1]%mod)%mod;
        f[0] = t1; f[1] = t0;
        pre = a[i];
    }
    return f[0];
}
vector<ll> v;
int main()
{
    ll n; int m; cin>>n>>m;
    fors(i, 0, m){ll x; scanf("%lld", &x); v.pb(x);}
    cout<<solve(n, m, v)<<endl;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/272579.html

標籤:其他

上一篇:站點接入相聲

下一篇:2020年年終總結

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more