第一次暑假集训

A - BBQ Easy

  • 题意:给你2*n个签子,两两配对,使得总共能串起来的配料最多, 每一串的配料的大小取决于两个签字中短的签字的长度
  • 做法:排序后相邻两个签子两两配对,就是最佳答案

代码

#include 
#include 
#include 
using namespace std;
int a[220];
int n;
int main(){
    cin >> n;
    for(int i=0;i<2*n;i++){
        cin >> a[i];
    }
    sort(a,a+n*2);
    int len=0;
    for(int i=0;i<n;i++){
        len+=a[2*i];
    }
    cout << len;
}

B - Range Product

  • 题意:给你a和b两个数,求出a*a+1…*b是正数负数还是0。
  • 做法:分类讨论即可

代码

#include 
#include 
#include 
using namespace std;
int main(){
   int a,b;
   cin >> a >> b;
   if(a>0){
       cout << "Positive";
       return 0;
   }
   if(a==0||b==0||(a<0&&b>0)){
       cout << "Zero";
       return 0;
   }
   if((b-a)%2){
      cout << "Positive"; 
   }else{
       cout << "Negative";
   }
}

C - Wanna go back home

  • 题意:给你n次操作,ESWN分别代表东南西北,为走的方向,但是不指定每一次走的距离,问最后是否能走会原点
  • 做法:没有指定距离,所以同一个轴上的方向同时出现过或者都没出现过就可以回到原点

代码

#include
#include
#include
using namespace std;
const int N=1e3+5;
char a[N];
int numn=0;
int nums=0;
int numw=0;
int nume=0;
bool check(){
    return (nums==numn||(nums&&numn))&&(nume==numw||(nume&&numw));
}
int main(){
    cin >> a;
    int n=strlen(a);
    
    for(int i=0;i<n;i++){
        if(a[i]=='S')nums++;
        if(a[i]=='E')nume++;
        if(a[i]=='W')numw++;
        if(a[i]=='N')numn++;
    }
    if(check()){
        cout << "Yes";
    
    }else{
        cout << "No";
    }
}

D - Divide a Cuboid

  • 题意:给你一个长方体,问你一刀把这个长方体一分为二,求两个小长方体的体积差最小是多少
  • 做法:如果长方体有一个边是偶数的话,就只要切哪个偶数的边,中间一份为二,体积差为0,没有体积为偶数的边,那么体积最小差就是最小两个边乘积

代码

#include 
#include 
#include 
using namespace std;
typedef long long ll;
ll a,b,c;
int main()
{
	cin >> a >> b >> c;
	if(a % 2 == 0 || b % 2 == 0 || c % 2 == 0)
		cout << 0 << endl;
	else
		cout << min(min(a*b,a*c),b*c) << endl;
	return 0;
}

E - STring

  • 题意:给你一个字符串,这个字符串只由S,T组成,并且两个字符的个数相等,每次都把最左的ST删除,问你最后的字符串长度为多少
  • 做法:用栈来处理,遇到T,就判断一下栈顶是否为S,如果为S就弹出即可

代码

#include
#include
#include
using namespace std;
 
int main(){
    string str ;
    while ( cin >> str ){
        int len = str.size() ;
        stack<char> container ;
        for ( int i = 0 ; i < str.size() ; i ++ ){
            if ( str[i] == 'T' ){
                if ( !container.empty() ){
                    if ( container.top() == 'S' ){
                        len -= 2 ;
                        container.pop() ;
                    }
                }else{
                    container.push(str[i]) ;
                }
            }else{
                container.push(str[i]) ;
            }
        }
        cout << len << endl ;
    }
    return 0 ;
}

F - Wall Bars

  • 题意:给你一个柱子,每一层都可以伸出一个横杆,有四个不同方向,每次可以从同一个方向的横杆上到达下一个距离小于h的横杠上面,柱子的总高度为n,如果能到达n-h+1~n之间的层数就成功,从地面开始,可以到达1 ~ h-1的任意高度的横杠,问最后的方案数是多少
  • 做法:令dp[i][a][b][c][d],表示为到达高度为i,a=0表示能到达这个层,a=1表示不能到达这一层,距离其他三个面最近的横杆的高度为b,c,d的方案数
  • 转移方程:
  • ①:不转变当前方向往上爬 ,那么只要在i+1层的a方向放置一个横杠即可:
    dp[i+1][a][min(b+1,h)][min(c+1,h)][min(d+1,h)]=
    (dp[i+1][a][min(b+1,h)][min(c+1,h)][min(d+1,h)]+dp[i][a][b][c][d])%inf;
  • ②:转变方向,则需要在i+1层放置要转变到的方向上放置一个横杠
  • dp[i+1][b+1<=h?1:0][a?1:h][min(c+1,h)][min(d+1,h)]=
  • (dp[i+1][b+1<=h?1:0][a?1:h][min(c+1,h)][min(d+1,h)]+dp[i][a][b][c][d])%inf;
    dp[i+1][c+1<=h?1:0][min(b+1,h)][a?1:h][min(d+1,h)]=
    (dp[i+1][c+1<=h?1:0][min(b+1,h)][a?1:h][min(d+1,h)]+dp[i][a][b][c][d])%inf;
    dp[i+1][d+1<=h?1:0][min(b+1,h)][min(c+1,h)][a?1:h]=
    (dp[i+1][d+1<=h?1:0][min(b+1,h)][min(c+1,h)][a?1:h]+dp[i][a][b][c][d])%inf;
  • tips:如果距离超过h了,就直接置为h,因为都是不可能爬上来的
  • 最后统计一下方案和即可

代码

#include
using namespace std;
const long long inf=1000000009;
long long dp[1001][2][31][31][31];
int main(){
    int n,h;
    cin>>n>>h;
    memset(dp,0,sizeof(dp));
    dp[0][1][0][0][0]=1;
    for(int i=0;i<n;i++){
        for(int a=0;a<2;a++){
            for(int b=0;b<=h;b++){
                for(int c=0;c<=h;c++){
                    for(int d=0;d<=h;d++){
                        dp[i+1][a][min(b+1,h)][min(c+1,h)][min(d+1,h)]=(dp[i+1][a][min(b+1,h)][min(c+1,h)][min(d+1,h)]+dp[i][a][b][c][d])%inf;
                        dp[i+1][b+1<=h?1:0][a?1:h][min(c+1,h)][min(d+1,h)]=(dp[i+1][b+1<=h?1:0][a?1:h][min(c+1,h)][min(d+1,h)]+dp[i][a][b][c][d])%inf;
                        dp[i+1][c+1<=h?1:0][min(b+1,h)][a?1:h][min(d+1,h)]=(dp[i+1][c+1<=h?1:0][min(b+1,h)][a?1:h][min(d+1,h)]+dp[i][a][b][c][d])%inf;
                        dp[i+1][d+1<=h?1:0][min(b+1,h)][min(c+1,h)][a?1:h]=(dp[i+1][d+1<=h?1:0][min(b+1,h)][min(c+1,h)][a?1:h]+dp[i][a][b][c][d])%inf;
                    }
 
                }
            }
        }
    }
    long long ans=0;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<=h;j++)
        {
            for(int k=0;k<=h;k++)
            {
                for(int l=0;l<=h;l++)
                {
                    if(i==1||j<h||k<h||l<h)
                    {
                        ans+=dp[n][i][j][k][l];
                        if(ans>inf)
                            ans-=inf;
                    }
                }
            }
        }
 
    }
    cout<<ans<<endl;
 
}

G - Bag of mice

  • 题意:这里有w只白鼠和b只黑鼠,龙和公主轮流从袋子里抓鼠,每次抓一只,抓到第一只白鼠的人获胜。当龙抓一只鼠时,袋子里会跑掉一只鼠,跑掉的鼠是等概率的。问公主获胜的概率。

  • 设有i只白鼠j只黑鼠的状态下公主获胜的概率是dp[i][j],这种状态可由一下三种状态得到:

  • ①:公主第一次就取得一只白鼠获胜,概率为i/(i+j);

  • ②:公主没有取到白鼠,取黑鼠的概率是j/(i+j),若公主要赢,下次龙一定取黑鼠,概率为(j-1)/(i+j-1),同时跑掉的是黑鼠,概率为(j-2)/(i+j-2),状态转移到dp[i][j-3];

  • ③:公主没有取到白鼠,取黑鼠的概率是j/(i+j),若公主要赢,下次龙一定取黑鼠,概率为(j-1)/(i+j-1),同时跑掉的是白鼠,概率为i/(i+j-2),状态转移到dp[i-1][j-2];

代码

#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1000+10;
double dp[maxn][maxn];
bool vis[maxn][maxn];
int w,b;
double dfs(int w,int b){
    if(w==0) return 0.0;
    if(b==0) return 1.0;
    if(vis[w][b]) return dp[w][b];
    vis[w][b] = 1;
    double res = w*1.0/(w+b);
    if(b>=3)
        res += (b*1.0/(w+b))*((b-1)*1.0/(w+b-1))*((b-2)*1.0/(w+b-2))*dfs(w,b-3);
    if(b>=2&&w>=1)
        res += (b*1.0/(w+b))*((b-1)*1.0/(w+b-1))*(w*1.0/(w+b-2))*dfs(w-1,b-2);
 
    return dp[w][b] = res;
}
int main(){
    memset(vis,0,sizeof vis);
    while(~scanf("%d%d",&w,&b)){
        printf("%.9lf\n",dfs(w,b));
    }
    return 0;
}

H - Positions in Permutations

  • 题意:给你1~n,这n个数字,如果一个位置i上放置了i-1或者i+1这个数字,就称i这个位置为好位置,求最后的排列使得好位置个数为m个的方案数
  • 做法:设f[i][j][0/1][0/1]表示在第1−i个数中,已经填了至少j个满足条件的数,0/1表示i是否已填,0/1表示i+1是否已填。
  • 决策三种:
    这一位不是好位置
    这一位填i−1
    这一位填i+1
    还要把最后没有钦定过的位置乘上,也就是阶乘
  • 再利用二项式反演求出结果
  • 具体参考大佬文章
    其中后面的二项式反演只要把n换成m即可

代码(转载)

#include
#include
#include
#include
#include
#include
#include
using namespace std;
int n,m;
const int N=1001;
const int mod=1e9+7;
inline void upd(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline int sum(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
int dp[2][2][N][N];//前 i 个位置 至少有 j 个 最后一个位置 i和i+1 有没有已经被钦定 的方案数.... 瞎算算情况下的方案数
// 只考虑当前位置放数的时候产生的幸运数
int fac[N],inv[N];
inline int fpow(int x,int k)
{
	register int ret=1;
	while(k){
		if(k&1) ret=1ll*ret*x%mod;
		x=1ll*x*x%mod;k>>=1;
	}
	return ret;
}
inline int C(int i,int j){return 1ll*fac[i]*inv[j]%mod*inv[i-j]%mod;}
int main()
{
	scanf("%d %d",&n,&m);
	dp[0][0][1][0]=1;
	dp[0][1][1][1]=1;
	for(register int i=2;i<=n;++i){
		
		for(register int j=0;j<i;++j){
			upd(dp[0][0][i][j],dp[0][0][i-1][j]);
			upd(dp[0][0][i][j],dp[1][0][i-1][j]);
			upd(dp[1][0][i][j],dp[0][1][i-1][j]);
			upd(dp[1][0][i][j],dp[1][1][i-1][j]);//不钦定当前位置
			
			//钦定 i+1 过来
			if(i<n){
				upd(dp[0][1][i][j+1],dp[0][0][i-1][j]);
				upd(dp[0][1][i][j+1],dp[1][0][i-1][j]);
				upd(dp[1][1][i][j+1],dp[0][1][i-1][j]);
				upd(dp[1][1][i][j+1],dp[1][1][i-1][j]);
			}
			//钦定了 i-1 过来
			upd(dp[0][0][i][j+1],dp[0][0][i-1][j]);
			upd(dp[1][0][i][j+1],dp[0][1][i-1][j]);
		}
	}
	fac[0]=1;
	for(register int i=1;i<=n;++i) fac[i]=1ll*fac[i-1]*i%mod;
	inv[0]=1;inv[n]=fpow(fac[n],mod-2);
	for(register int i=n-1;i;--i) inv[i]=(1ll*inv[i+1]*(i+1))%mod;
	register int ans=0;
	for(register int i=m,p=1;i<=n;++i,p=-p){//二项式反演
		register int S=1ll*sum(dp[0][0][n][i],dp[1][0][n][i])*fac[n-i]%mod;
		ans+=(1ll*S*C(i,m)%mod*p+mod)%mod;
		if(ans>=mod) ans-=mod;
	}
	printf("%d\n",ans);
}

I - Anya and Cubes

  • 题意:给你n个数字,取出任意数字,使得值和为s,还有k使得某个数字的值变为该数字阶乘的机会,求所有的方案数
  • 做法:直接dfs不可能,所以要用折半的方法,先对n个数字的前一半部分进行dfs,把他们的和存入unordered_map中,再对于后边部分进行dfs,并且判断一下是否map中存在一个数字使得和改数字的和就是s,答案加上这个map中数字的个数即可

代码

#include 
#include 
#include 
#include 
using namespace std;
int k,a[30];
long long S,ans,f[30];
map<long long,int> mp[30];
void dfs1(int st,int en,int op,long long sum){
    if(sum>S||op>k)
    return;
    if(st==en){
        mp[op][sum]++;
        return;
    }
    dfs1(st+1,en,op,sum);
    dfs1(st+1,en,op,sum+a[st]);
    if(a[st]<20)
    dfs1(st+1,en,op+1,sum+f[a[st]]);
}
void dfs2(int st,int en,int op,long long sum){
    int i;
    if(sum>S||op>k)
    return;
    if(st==en){
        for(i=0;i<=k-op;i++)                    
        if(mp[i].count(S-sum))
        ans+=mp[i][S-sum];
        return;
    }
    dfs2(st+1,en,op,sum);
    dfs2(st+1,en,op,sum+a[st]);
    if(a[st]<20)
    dfs2(st+1,en,op+1,sum+f[a[st]]);
}
int main(){
    int i,n;
    f[0]=1;
    for(i=1;i<=20;i++)
    f[i]=f[i-1]*i;
    while(scanf("%d%d%I64d",&n,&k,&S)!=EOF){
        for(i=0;i<n;i++){                       
            scanf("%d",&a[i]);
            mp[i].clear();
        }
        ans=0;
        dfs1(0,n/2,0,0);
        dfs2(n/2,n,0,0);
        cout << ans;
    }
    return 0;
}

J - Fence

  • 题意:给你n个木板,绿色染料a克,红色染料b克,一克染一厘米长度的木板且一个木板必须同一颜色,代价为相邻的两个木板挨着的不同的颜色的长度(厘米)的和,问最小代价?
  • 做法:令dp[i][j][0/1]表示涂了i块木板,用了j个0/1个涂料的最小代价,0表示绿色,1表示红色
  • ①:如果当前涂成红色,则dp[i][j][1]=min(dp[i-1][j-h[i]][1],dp[i-1][sum-j][0]+min(h[i],h[i-1]));;前面一个是上一块木板也是红色的情况,后面一个是上一块木板是绿色,sum存下的是已经用了的涂料总和,也是已涂木板高度总和
  • ②:绿色同理,可得方程dp[i][j][0]=min(dp[i-1][j-h[i]][0],dp[i-1][sum-j][1]+min(h[i],h[i-1]));

代码

#include 
using namespace std;
typedef long long LL;
typedef long long ll ;
const int N=200+7;
int n,m,a,b,h[N];
int dp[N][40040][2];
//前i个木板,用了j个0/1燃料 ,0:绿色,1:蓝色
int main()
{
     freopen("input.txt","r",stdin);
     freopen("output.txt","w",stdout);
    scanf("%d",&n);
    scanf("%d%d",&a,&b);
    for(int i=1;i<=n;i++)
	{
		scanf("%d",&h[i]);
	}
	memset(dp,0x3f,sizeof(dp));
	dp[0][0][0]=0;
	dp[0][0][1]=0;
	ll sum=0;
	for(int i=1;i<=n;i++)
	{
		sum+=h[i];
		for(int j=h[i];j<=sum;j++)
		{
			if(j<=a)//染绿色
			{
				dp[i][j][0]=min(dp[i-1][j-h[i]][0],dp[i-1][sum-j][1]+min(h[i],h[i-1]));
			}
			if(j<=b)//染红色
			{
				dp[i][j][1]=min(dp[i-1][j-h[i]][1],dp[i-1][sum-j][0]+min(h[i],h[i-1]));
			}
		}
	}
	int ans=1e9;
	for(int i=1;i<=a;i++)
	{
		ans=min(ans,dp[n][i][0]);
	}
	for(int i=1;i<=b;i++)
	{
		ans=min(ans,dp[n][i][1]);
	}
	if(ans==1e9) ans=-1;
	printf("%d\n",ans);
	
    return 0 ;
}
 

K - Mike and Friends

  • 做法:后缀自动机+树状数组维护right集合

代码(转载)

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=400010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
struct Seg{int l,r,lc,rc,c;}tr[Maxn<<1];
int trlen=0;
void build(int l,int r)
{
    int t=++trlen;
    tr[t].l=l;tr[t].r=r;tr[t].c=0;
    if(l<r)
    {
        int mid=l+r>>1;
        tr[t].lc=trlen+1;build(l,mid);
        tr[t].rc=trlen+1;build(mid+1,r);
    }
}
void add(int now,int p)
{
    if(tr[now].l==tr[now].r){tr[now].c++;return;}
    int mid=tr[now].l+tr[now].r>>1,lc=tr[now].lc,rc=tr[now].rc;
    if(p<=mid)add(lc,p);else add(rc,p);
    tr[now].c=tr[lc].c+tr[rc].c;
}
LL query(int now,int l,int r)
{
    if(tr[now].l==l&&tr[now].r==r)return tr[now].c;
    int mid=tr[now].l+tr[now].r>>1,lc=tr[now].lc,rc=tr[now].rc;
    if(r<=mid)return query(lc,l,r);
    else if(l>mid)return query(rc,l,r);
    return query(lc,l,mid)+query(rc,mid+1,r);
}
int tot=1,last=1;
int par[Maxn],son[Maxn][26],mx[Maxn];
int lim[200010];LL Right[Maxn];
void extend(int x)
{
    int p=last,np=++tot;mx[np]=mx[p]+1;Right[np]=1;
    while(p&&!son[p][x])son[p][x]=np,p=par[p];
    if(!p)par[np]=1;
    else
    {
        int q=son[p][x];
        if(mx[p]+1==mx[q])par[np]=q;
        else
        {
            int nq=++tot;mx[nq]=mx[p]+1;
            for(int i=0;i<26;i++)son[nq][i]=son[q][i];
            par[nq]=par[q];
            par[q]=par[np]=nq;
            while(son[p][x]==q)son[p][x]=nq,p=par[p];
        }
    }
    last=np;
}
struct Edge{int y,next;}e[Maxn];
int Last[Maxn],Len=0;
void ins(int x,int y){int t=++Len;e[t].y=y;e[t].next=Last[x];Last[x]=t;}
int dfn=0,in[Maxn],out[Maxn];
void dfs(int x)
{
    in[x]=++dfn;
    for(int i=Last[x];i;i=e[i].next)dfs(e[i].y);
    out[x]=dfn;
}
struct Query{int p,x,id,f;}q[1000010];
bool cmp(Query a,Query b){return a.p<b.p;}
LL ans[500010];
int n,Q;
string s[200010];int len[200010];
int pos[200010];
int main()
{
    n=read(),Q=read();
    lim[0]=1;
    for(int i=1;i<=n;i++)
    { 
        cin>>s[i];
        len[i]=s[i].size();
        last=1;
        for(int j=0;j<len[i];j++)extend(s[i][j]-'a');
        lim[i]=tot;
    }
    for(int i=2;i<=tot;i++)ins(par[i],i);
    dfs(1);build(1,dfn);
    for(int i=1;i<=n;i++)
    {
        int now=1;
        for(int j=0;j<len[i];j++)
        now=son[now][s[i][j]-'a'];
        pos[i]=now;
    }
    for(int i=1;i<=Q;i++)
    {
        q[(i<<1)-1].p=read()-1;q[i<<1].p=read();
        q[(i<<1)-1].x=q[i<<1].x=read();
        q[(i<<1)-1].id=q[i<<1].id=i;
        q[(i<<1)-1].f=-1,q[i<<1].f=1;
    }
    sort(q+1,q+1+(Q<<1),cmp);
    int now=1;
    while(!q[now].p)now++;
    for(int i=1;i<=n;i++)
    {
        for(int j=lim[i-1]+1;j<=lim[i];j++)
        if(Right[j])add(1,in[j]);
        while(now<=(Q<<1)&&q[now].p==i)
        ans[q[now].id]+=q[now].f*query(1,in[pos[q[now].x]],out[pos[q[now].x]]),now++;
    }
    for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
}

L - A String Game

  • 后缀自动机+SG函数

转载

#include
using namespace std;

const int maxn = 100005;
int n;
char s[maxn];
char t[105][maxn];

struct Sam {
	int next[maxn << 1][26];
	int link[maxn << 1], step[maxn << 1];
	int sg[maxn << 1], vis[30];
	int a[maxn], b[maxn << 1];
	int sz, last, len, root;

	void init() {
		for(int i = 0; i <= sz; i++) {
			memset(next[i], 0, sizeof(next[i]));
		}
		memset(a, 0, sizeof(a));
		root = sz = last = 1;
	}

	void add(int c) {
		int p = last;
		int np = ++sz;
		last = np;

		step[np] = step[p] + 1;
		while(!next[p][c] && p) {
			next[p][c] = np;
			p = link[p];
		}

		if(p == 0) {
			link[np] = root;
		} else {
			int q = next[p][c];
			if(step[p] + 1 == step[q]) {
				link[np] = q;
			} else {
				int nq = ++sz;
				memcpy(next[nq], next[q], sizeof(next[q]));
				step[nq] = step[p] + 1;
				link[nq] = link[q];
				link[q] = link[np] = nq;
				while(next[p][c] == q && p) {
					next[p][c] = nq;
					p = link[p];
				}
			}
		}
	}

	void build() {
		init();
		for(int i = 0; s[i] ; i++) {
			add(s[i] - 'a');
		}
		for(int i = 1; i <= sz; i++) {
			a[step[i]]++;
		}
		for(int i = 1; i <= step[last]; i++) {
			a[i] += a[i - 1];
		}
		for(int i = 1; i <= sz; i++) {
			b[a[step[i]]--] = i;
		}
	}

	void grundy() {
		sg[last] = 0;
		for(int i = sz; i > 1; i--) {
			int e = b[i];
			int g = 0;
			memset(vis, 0, sizeof(vis));
			for(int j = 0; j < 26; j++) {
				if(!next[e][j]) {
					continue;
				}
				vis[sg[next[e][j]]] = 1;
			}
			while(vis[g]) {
				++g;
			}
			sg[e] = g;
		}
	}

	void solve() {
		build();
		grundy();
		int sum = 0;
		for(int i = 1; i <= n; i++) {
			int p = root;
			for(int j = 0; t[i][j] ; j++) {
				p = next[p][t[i][j] - 'a'];
			}
			sum ^= sg[p];
		}
		printf("%s\n", sum ? "Alice" : "Bob");
	}

} sam;

int main() {
	while(~scanf("%s", s)) {
		scanf("%d", &n);
		for(int i = 1; i <= n; i++) {
			scanf("%s", t[i]);
		}
		sam.solve();
	}
	return 0;
}

你可能感兴趣的:(第一次暑假集训)