暑假集训日记——7.15(ST表+单调栈/单调队列+codeforce)

博客内容包括多个算法题目的解析,涉及博弈论和数据结构。如C. From S To T是模拟题,B. Yet Another Crosses Problem通过模拟解决。1-2-K Game的博弈问题中,通过找规律确定胜负状态。Codeforces Round #278 B - Strip 使用动态规划结合单调队列和ST表求解。最后介绍了二维ST表的应用。
摘要由CSDN通过智能技术生成

C. From S To T
题意:
给你三个字符串s,p,t,问你能否在s串中的任何位置加入p串中的任何字符,从而使s串变成t串?

题解:模拟题

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<functional>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair

const int N=1e7+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,t;

int a[30],b[30];
string str1,str2,str3 ;

bool contains(string t, string s) {//判断s是否为t的子串
	int i = 0;
	for (char c : s) {
		if (i == t.size()) return false;
		while (i < t.size() && c != t[i]) {
			i++;
		}
		i++;
	}
	return i <= t.size();
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>t;
    while(t--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        vector<int>q[30],p[30];
        cin>>str1>>str2>>str3;
        for(int i=0;i<str1.size();i++)
        {
            q[str1[i]-'a'].push_back(i);
            a[str1[i]-'a']++;
        }
        for(int i=0;i<str2.size();i++)
        {
            p[str2[i]-'a'].push_back(i);
            b[str2[i]-'a']++;
        }
        for(int i=0;i<str3.size();i++)
        {
            a[str3[i]-'a']++;
        }
        int flag=0;
        flag=!contains(str2, str1);
        if(flag==1)
            cout<<"NO"<<endl;
        else
        {
            for(int i=0;i<26;i++)
            {
                if(a[i]<b[i])
                {
                    flag=1;break;
                }
            }
            if(flag==1) cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
        }
    }
}

B. Yet Another Crosses Problem
题意:找补充次数最少的十字架

题解:模拟
注意:
1.空间开太大memset超时
2.main函数内建立数组,变量作范围(没这么写过…),用memset清空一下
结构体写着写着就忘记干啥的了…自闭ing

版本一: 1279 ms,
用sort找到最大值反而慢,跟数据有关吧,没有排序的必要,反而拖时长

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<functional>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair

const int N=5e4+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,t;

struct note
{
    int x,id;
}a[N],b[N];

bool cmp(note a,note b)
{
    return a.x<b.x;
}
string str;

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>t;
    while(t--)
    {
       memset(a,0,sizeof(a));
       memset(b,0,sizeof(b));
       cin>>n>>m;
       int vis[n][m];
       memset(vis,0,sizeof(vis));
       for(int i=0;i<n;i++)
       {
           cin>>str;
           for(int j=0;j<m;j++)
           {
               if(str[j]=='*')
                a[j].x++,a[j].id=j,b[i].x++,b[i].id=i;
               if(str[j]=='.')
                vis[i][j]=1;
           }
       }
       sort(a,a+m,cmp);
       sort(b,b+n,cmp);
       int flag=0;
       for(int i=n-1;i>=0;i--)
       {
           if(b[i].x==b[n-1].x)
           {
               for(int j=m-1;j>=0;j--)
               {
                   if(a[j].x==a[m-1].x)
                   {
                       if(vis[b[i].id][a[j].id]==1)
                       {

                           flag=1;
                           //cout<<i<<" "<<j;
                           break;
                       }
                   }
                   else
                    break;
               }
           }
           else break;
       }
        if(flag==0)
            cout<<n+m-(a[m-1].x+b[n-1].x)<<endl;
        else
            cout<<n+m-(a[m-1].x+b[n-1].x)-1<<endl;
    }
}

版本二:545 ms
反而暴力快一倍…

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<functional>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
 
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair
 
const int N=5e4+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,t;
 
int a[N],b[N];
 
char str[N];
 
int main()
{
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    scanf("%d",&t);
    while(t--)
    {
       memset(a,0,sizeof(a));
       memset(b,0,sizeof(b));
       scanf("%d%d",&n,&m);
       int vis[n][m];
       memset(vis,0,sizeof(vis));//��ʼ��
       for(int i=0;i<n;i++)
       {
           scanf("%s",str);
           for(int j=0;j<m;j++)
           {
               if(str[j]=='*')
                a[j]++,b[i]++;
               if(str[j]=='.')
                vis[i][j]=1;
           }
       }
       int flag=0,ans=N;
       for(int i=0;i<n;i++)
       {
               for(int j=0;j<=m;j++)
               {
                       flag=0;
                       if(vis[i][j]==1)
                       {
                           flag=1;
                       }
                       if(flag==0)
                            ans=min(n+m-(a[j]+b[i]),ans);
                       else
                            ans=min(n+m-(a[j]+b[i])-1,ans);
                       if(ans==0)
                          break;
 
               }
               if(ans==0)
                break;
 
       }
 
       printf("%d\n",ans);
    }
}

1-2-K Game
题解:
Let’s determine for each cell whether it’s winning or losing position (we can do it since the game is symmetric and doesn’t depend on a player). The 0-th cell is obviously losing, the 1-st and 2-nd ones is both winning, since we can move to the 0-th cell and put our opponent in the losing position (here comes criterion: the position is winning if and only if there is a move to the losing position).

If k is large enough, then the 0-th, 3-rd, 6-th, 9-th… are losing. So here comes divisibility by 3. If then this move doesn’t change anything, since if then so it’s not the move to the losing position, so x doesn’t become the winning one.

Otherwise, if then the k-th positions becomes winning, but the (k + 1)-th cell is losing (all moves are to (k - 1)-th, k-th or 1-st cells and all of them are winning). The (k + 2)-th and (k + 3)-th cells are winning and so on. In the end, we came up with cycle of length k + 1 where position divisible by 3 except k are losing.

All we need to do is small case work.

博弈题,想不明白的话,就找规律(不用想明白了,就直接找规律吧,列举一些情况然后观察就好了,想原理脑袋疼…

1.当 k k k% 3 ! = 0 3!=0 3!=0 的时候很好理解,和巴什博弈差不多
这时候不会有一个原来的必败态因为走k步而到达另一个必败态,也就是说这时候k对胜负是没有影响的,可以直接认为k不存在,输赢和上面一样。
2.主要是 k k k% 3 = 0 3=0 3=0的时候,怎么考虑

__ 先列举一下前 k + 1 k+1 k+1个元素:( k = 6 k=6 k=6, T T T表示赢)
0_1_2_3_4_5_6_7
0_T_T_0_T_T_T_0
上图的依据:
如果 m m m点是必败点,那么 m + 1 , m + 2 , m + k m+1,m+2,m+k m+1m+2m+k就是必胜点,因为如果在这个点上面,我们可以通过走 1 , 2 , k 1,2,k 12k步走到一个必败点,从而赢得胜利。
如果一个点m为必败点那么,这个点 m − 1 , m − 2 , m − k m-1,m-2,m-k m1,m2,mk一定为必胜点,因为这个点只能由后手走到。
所以总结一下就是:能被先手走到的点是 T T T,仅能被后手走到的点是 0 0 0
猜想:从必败点仅通过一步,如何都走不到的第一个点一定为必败点。

所以胜利的条件就是开始的时候是位于胜利的格子上。然后推理可得结论,如果思考不出原理,也可以像上图一样枚举状态,最后发现有一个 k + 1 k+1 k+1为周期的循环。

#include <bits/stdc++.h>
using namespace std;

int n, k;

inline bool read() {
	if(!(cin >> n >> k))
		return false;
	return true;
}

void solve() {
	bool win = true;
	if(k % 3 == 0) {
		int np = n % (k + 1);
		if(np % 3 == 0 && np != k)
			win = false;
	} else {
		int np = n % 3;
		if(np == 0)
			win = false;
	}
	
	puts(win ? "Alice" : "Bob");
}

int main(){
	int T; cin >> T;
	while(T--) {
		read();
		solve();
	}
	return 0;
}

Codeforces Round #278 B - Strip
题意:给一个数组,然后要求把这个数组分成几段,每一段最值差不超过S,每一段的区间长度不少于L,问最少能够分成多少段?

题解:(dp+单调队列+ST表)
We can use dynamic programming to solve this problem.
Let f[i] denote the minimal number of pieces that the first i numbers can be split into. g[i] denote the maximal length of substrip whose right border is i(included) and it satisfy the condition.

Then f[i] = min(f[k]) + 1, where i - g[i] ≤ k ≤ i - l.

We can use monotonic queue to calculate g[i] and f[i]. And this can be implemented in O(n)
We can also use sparse table or segment tree to solve the problem, the time complexity is or (It should be well-implemented).
注意:INF ——0x3f3f3f3f

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<functional>
#include<set>
#include<map>
#include<stack>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
 #define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+7;
struct note
{
    int x,id;
}q[maxn];

int st[maxn][20],st1[maxn][20],a[maxn],n,dp[maxn];
int head,tail;

void init(int n)
{
    for(int i=1;i<=n;i++)  st1[i][0]=st[i][0]=a[i];//初始化,从i点开始2^j长度的极值
    for(int j=1;j<20;j++)
        for(int i=1;i<=n;i++)
            if(i+(1<<(j-1))<=n)
                st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]),
                st1[i][j]=max(st1[i][j-1],st1[i+(1<<(j-1))][j-1]); //最值
}

int query(int l,int r)
{
    int len=log2(r-l+1);
    return max(st1[l][len],st1[r-(1<<len)+1][len])-min(st[l][len],st[r-(1<<len)+1][len]);
}

int main()
{
   ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
   int s,len; cin>>n>>s>>len;
    for(int i=1;i<=n;i++) cin>>a[i];
    init(n);
    int l,r,ans;
    if(len>n||query(1,len)>s) return cout<<-1<<endl,0;
    memset(dp,INF,sizeof(dp)); dp[0]=0; q[tail].id=0,q[tail].x=0,tail++;
    dp[len]=1;
    for(int i=len+1;i<=n;i++)
    {
        l=1,r=i-len+1,ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(query(mid,i)<=s) r=mid-1,ans=mid;
            else l=mid+1;
        }
        //单调队列维护到i为止的划分的最小块的个数
        while(head<tail&&q[tail-1].x>=dp[i-len]) tail--;   ///
        q[tail].x=dp[i-len],q[tail].id=i-len,tail++;       ///
        if(ans!=-1)
        {
            while(head<tail&&q[head].id<ans-1) head++;     ///
            if(head<tail)                                  ///
                dp[i]=min(dp[i],q[head].x+1);//动态转移方程
        }

    }
    if(dp[n]==INF) dp[n]=-1;
    cout<<dp[n]<<endl;

}

G - Check Corners
二维ST表的模板题

#include<iostream>
#include<cmath>
#include<algorithm>
#define INF 0x3f3f3f3f

using namespace std;
int dp[301][301][9][9];
int a[301][301];

void init(int n,int m)
{
    for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                dp[i][j][0][0]=a[i][j];
        int t1=log(n)/log(2)+1,t2=log(m)/log(2)+1;
        for(int l=0;l<t1;++l)
            for(int k=0;k<t2;++k)
                if(l+k)
                    for(int i=1;i<=n-(1<<l)+1;++i)
                        for(int j=1;j<=m-(1<<k)+1;++j)
                            if(l==0)
                                dp[i][j][l][k]=max(dp[i][j][l][k-1],dp[i][j+(1<<(k-1))-1+1][l][k-1]);
                            else
                                dp[i][j][l][k]=max(dp[i][j][l-1][k],dp[i+(1<<(l-1))-1+1][j][l-1][k]);
}

int query(int x,int y,int z,int v)
{
    int t1=log(z-x+1)/log(2),t2=log(v-y+1)/log(2);
    int ans=max(dp[x][y][t1][t2],dp[z-(1<<t1)+1][v-(1<<t2)+1][t1][t2]);
        ans=max(dp[x][v-(1<<t2)+1][t1][t2],ans);
        ans=max(ans,dp[z-(1<<t1)+1][y][t1][t2]);
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n,m,q;
    while(cin>>n>>m)
    {
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                cin>>a[i][j];
        init(n,m);
        cin>>q;
        while(q--)
        {
            int x,y,z,v;
            cin>>x>>y>>z>>v;
            int ans=query(x,y,z,v);
            cout<<ans<<" ";
            if(a[x][y]==ans||a[z][v]==ans||a[x][v]==ans||a[z][y]==ans)cout<<"yes"<<endl;
            else cout<<"no"<<endl;
        }
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值