2016 Multi-University Training Contest 6 题解

我只能说:

A Boring Question

下面公式重复了一行

\[\sum\_{0\leq k\_{1},k\_{2},\cdots k\_{m}\leq n}\prod\_{1\leq j< m}\binom{k\_{j+1}}{k\_{j}} \\\ =\sum\_{0\leq k\_{1}\leq k\_{2}\leq\cdots \leq k\_{m}\leq n}\prod\_{1\leq j< m}\binom{k\_{j+1}}{k\_{j}} \\\ =\sum\_{k\_{m}=0}^{n}\sum\_{k\_{m-1}=0}^{k\_{m}}\cdots \sum\_{k\_{1}=0}^{k\_{2}}\prod\_{1\leq j< m}\binom{k\_{j+1}}{k\_{j}} \\\ =\sum\_{k\_{m}=0}^{n}\left \\{ \binom{k\_{m}}{k\_{m-1}} \sum\_{k\_{m-1}=0}^{k\_{m}} \left \\{ \binom{k\_{m-1}}{k\_{m-2}} \cdots \sum\_{k\_{1}=0}^{k\_{2}}\binom{k\_{2}}{k\_{1}} \right \\} \right \\} \\\ =\sum\_{k\_{m}=0}^{n}\left \\{ \binom{k\_{m}}{k\_{m-1}} \sum\_{k\_{m-1}=0}^{k\_{m}} \left \\{ \binom{k\_{m-1}}{k\_{m-2}} \cdots \sum\_{k\_{1}=0}^{k\_{2}}\binom{k\_{2}}{k\_{1}} \right \\} \right \\} \\\\ =\sum\_{k\_{m}=0}^{n}\left \\{ \binom{k\_{m}}{k\_{m-1}} \sum\_{k\_{m-1}=0}^{k\_{m}} \left \\{ \binom{k\_{m-1}}{k\_{m-2}} \cdots \sum\_{k\_{2}=0}^{k\_{3}}\binom{k\_{3}}{k\_{2}}2^{k\_{2}} \right \\} \right \\} \\\\ =\sum\_{k\_{m}=0}^{n}m^{k\_{m}} \\\\ =\frac{m^{n+1} - 1}{m - 1} \\\\ \]

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

long long powt(long long a,long long b)
{
    long long r = 1;
    while(b)
    {
        if(b & 1) r = r * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return r;
}

int main()
{
    long long t,n,m;
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld%lld",&n,&m);
        printf("%lld\n",((powt(m,n + 1) - 1) * powt(m - 1,mod - 2) % mod + mod) % mod);
    }
    return 0;
}

A Simple Chess

CF560E,<----原题

这是一道容斥+Lucas定理的水题。

tips:终点可能有障碍物

#include <algorithm>
#include <cstdio>
#include <vector>
#include <iostream>

using namespace std;

const int mod = 110119;

long long fac[mod];
long long inv[mod];
long long facinv[mod];

void init()
{
    fac[0] = fac[1] = inv[1] = facinv[0] = facinv[1] = 1;
	for(int i = 2; i < mod; ++i) {
		fac[i] = fac[i - 1] * i % mod;
		inv[i] = inv[mod % i] * (mod - mod / i) % mod;
		facinv[i] = facinv[i - 1] * inv[i] % mod;
	}
}

long long C(long long n, long long m)
{
	if(n < 0 || m < 0 || m > n)
		return 0;
	return fac[n] * facinv[m] % mod * facinv[n - m] % mod;
}

long long Lucas(long long n, long long m)
{
	if(n < 0 || m < 0 || m > n)
		return 0;
	long long res = 1;
	while(n || m) {
		res = res * C(n % mod, m % mod) % mod;
		n /= mod;
		m /= mod;
	}
	return res;
}

long long Solve(long long n, long long m) { return Lucas(n + m, n); }

int main()
{
	init();
	long long n, m;
	int r;
	int kase = 0;
	while(scanf("%lld %lld %d", &n, &m, &r) == 3) {
		vector<pair<long long, long long>> v;
		while(r--) {
			long long x, y;
			scanf("%lld %lld", &x, &y);
			v.emplace_back(x, y);
		}
		v.emplace_back(1, 1);
		v.emplace_back(n, m);
		sort(v.begin(), v.end());
		vector<long long> dp(v.size(), 0);
		dp[0] = mod - 1;
		for(int i = 1; i < v.size(); ++i)
			for(int j = 0; j < i; ++j) {
				if(v[i].first < v[j].first || v[i].second < v[j].second ||
				        (v[i].first + v[i].second - v[j].first - v[j].second) % 3)
					continue;
				long long step =
				    (v[i].first + v[i].second - v[j].first - v[j].second) / 3;
				dp[i] = (dp[i] + mod -
				         dp[j] * Solve(v[i].first - v[j].first - step,
				                       v[i].second - v[j].second - step) %
				         mod) %
				        mod;
			}
		printf("Case #%d: %d\n", ++kase, (int)dp.back());
	}
	return 0;
}

A Simple Nim

sg[0]=0

当x=8k+7时sg[x]=8k+8,

当x=8k+8时sg[x]=8k+7,

其余时候sg[x]=x;(k>=0)

打表找规律可得,数学归纳法可证。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        int ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            int x,sg;
            scanf("%d",&x);
            if(x%8!=0&&x%8!=7)
                sg=x;
            else
                if(x%8==0) sg=x-1;else sg=x+1;
            ans^=sg;
        }
        if(ans) printf("First player wins.\n");else printf("Second player wins.\n");
    }
    return 0;
}

Magic Number

对于bi来说,二进制的第i位一定为1,大于i位一定是0。将二进制的每一位看作一个结点构成一棵树,使得结点i到根的路径上的所有结点编号表示bi二进制中为1的位置。已知b1到bi-1所构成的树,新增加一个结点bi,只需要求构成bi的所有bj的最近公共祖先,将结点i放到该祖先的儿子的位置。对于询问ci,只需要求构成di的所有bj的到根路径的并,答案为路径并的结点数。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

#define MAXN 100010
#define MAXM 1000010
typedef long long LL;

int n,m,q,r,qlen[MAXN];
int dfn[MAXN],def;
int que[MAXN];
int L[MAXN],fa[MAXN][20];
LL u[MAXM];

struct node
{
    int v;
    node *next;
}edge[MAXN*2];
node *cnt=&edge[0];
node *adj[MAXN];

void Addedge(int u,int v)
{
    node *p=++cnt;
    p->v=v;
    p->next=adj[u]; adj[u]=p;
}

void DFS(int u)
{
    dfn[u]=++def;
    for(node *p=adj[u];p;p=p->next){
        DFS(p->v);
    }
}


bool CMP(int a,int b)
{
    return dfn[a]<dfn[b];
}

void Getfather(int i)
{
    L[i]=L[fa[i][0]]+1;
    Addedge(fa[i][0],i);
    for(int j=1;(1 << j)<=n;++j)
        fa[i][j]=fa[fa[i][j-1]][j-1];
}

int LCA(int x,int y)
{
    int i,Lg;
    if(L[x]<L[y]){
        x+=y; y=x-y; x=x-y;
    }
    for(Lg=1;(1 << Lg)<=L[x];++Lg); --Lg;
    for(i=Lg;i>=0;--i)
        if(L[x]-(1 << i) >=L[y])
            x=fa[x][i];
    if(x==y) return x;
    for(i=Lg;i>=0;--i)
        if(fa[x][i] && fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void GET(LL &num)
{
    char CH; bool F=false;
    for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
    for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
    F && (num=-num);
}

void GET(int &num)
{
    char CH; bool F=false;
    for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
    for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
    F && (num=-num);
}

void Init()
{
    memset(adj,0,sizeof(adj));
    cnt=&edge[0]; def=0;
    int i,k,f=-1; LL j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i) GET(u[i]);//scanf("%I64d",u+i);
    sort(u+1,u+1+m);
    for(i=1,j=0,k=1;i<=m;++i){
        while(u[i]>=j+k){
            if(f==-1) f=0;
            fa[k][0]=f; Getfather(k);
            j+=k++; f=-1;
        }
        if(u[i]-j+1!=k){
            if(f==-1) f=u[i]-j+1;
            else f=LCA(f,u[i]-j+1);
        }
    }
    while(k<=n){
        if(f==-1) f=0;
        fa[k][0]=f; Getfather(k);
        j+=k++; f=-1;
    }
}

void Query()
{
    int i,k,cnt,p; LL j;
    scanf("%d%d",&q,&r);
    for(i=1;i<=q;++i) GET(qlen[i]);//scanf("%d",qlen+i);
    for(i=1;i<=r;++i) GET(u[i]);//scanf("%I64d",u+i);
    sort(u+1,u+1+r);
    for(i=1,j=0,k=1;i<=r;++i){
        while(u[i]>=j+qlen[k]){
            sort(que+1,que+1+que[0],CMP);
            for(p=2,cnt=que[0]?L[que[1]]:0;p<=que[0];++p)
                cnt+=L[que[p]]-L[LCA(que[p-1],que[p])];
            printf("%d\n",cnt);
            j+=qlen[k++]; que[0]=0;
        }
        if(u[i]-j+1<=n) que[++que[0]]=u[i]-j+1;
    }
    while(k<=q){
        sort(que+1,que+1+que[0],CMP);
        for(p=2,cnt=que[0]?L[que[1]]:0;p<=que[0];++p)
        cnt+=L[que[p]]-L[LCA(que[p-1],que[p])];
        printf("%d\n",cnt);
        j+=qlen[k++]; que[0]=0;
    }
}

int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
//    int size = 256 << 20; // 256MB
//    char *p = (char*)malloc(size) + size;
//    __asm__("movl %0, %%esp\n" :: "r"(p));
    int i,t;
    for(scanf("%d",&t),i=1;i<=t;++i){
        printf("Case #%d:\n",i);
        Init();
        DFS(0);
        Query();
    }
    return 0;
}

Master Zhu

by liao772002(全场最佳)

考虑一个朴素的解法:2^n枚举在哪些行放置棋子,并判断是否可以通过这些棋子填充其它的列。由于区间的边界非递减,那么处理出要放的列,每次贪心地放置编号最小的需要放的列必然是最优的。

那么考虑一个dp的做法:dp1[i][j][k]表示现在做了前i行且有一些行没填,需要把jk列都放置棋子才能把前i行的所有格子满足条件,dp2[i][j]表示填了前i行且有一些行没填,但是所有格子都已经满足条件,且之后只需要填第j列之后的列,dp3[i][j][k]表示填了前i行,且下一个没填的行是第k行,现在填了L[k]j列(j = L[K] - 1表示一列都没有填)。

考虑dp1的转移,对于第i行,只要枚举左边界不小于L(i+1)且右边界不大于R(i+1)的列区间没填,因为若左边界小于L(i+1),那么这些列以后无论怎么样都无法被填充。然后分情况讨论转移到i+1的即可。

考虑dp2的转移,枚举下一个没填的行k,并分情况转移到相应的dp2[k][R[k]]和dp3[i][k][max(j,L[k] - 1)]

考虑dp3的转移,也是分情况讨论转移,具体实现看标程。时间复杂度O(N*(2NM + M^2))

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 105;

int n , m;
int r[N], l[N];
int dp1[N][N][N],dp2[N][N],dp3[N][N][N];
///dp1[i][j][k]:表示现在做前i行,有的行没填,需要把j~k列都填上
///dp2[i][j]:表示填了前i行,且前j列都填了(就是之后只要填第j列之后的列)
///dp3[i][j][k]:表示填了前i行,下一个没填的行是第k行,现在填了第k行的前j列

void update(int &o, int p) { o = min(o, p); }

void init()
{
    scanf("%d %d",&n, &m);
    for(int i = 1;i <= n;i++)
      scanf("%d %d",&l[i],&r[i]);
}

void work()
{
    memset(dp1, 0x3f, sizeof(dp1));
    memset(dp2, 0x3f, sizeof(dp2));
    memset(dp3, 0x3f, sizeof(dp3));

    int ans = n;

    dp1[1][l[1]][r[1]] = 0;
    dp2[0][0] = 0;
    r[0] = 0;l[0] = -1;

    for (int i = 0; i < n; i++)
    {
        ///dp1
        for(int j = l[i + 1];j <= r[i];j ++)///l[i + 1] <= j
          for(int k = j;k <= r[i];k ++)
          if(dp1[i][j][k] <= n)
          {
              //printf("dp1[%d][%d][%d] = %d\n",i,j,k,dp1[i][j][k]);
              if(j < k)
              {
                  update( dp1[i + 1][j + 1][k] , dp1[i][j][k] + 1);
              }
              if(j == k)
              {
                  update( dp2[i + 1][j] , dp1[i][j][k] + 1);
              }
              update(dp1[i + 1][j][r[i + 1]] , dp1[i][j][k]);
          }

        ///dp2
        for(int j = 0;j <= r[i];j++)
        if(dp2[i][j] <= n)
        {
          //  printf("dp2[%d][%d] = %d\n",i,j,dp2[i][j]);
            for(int k = i + 1;k <= n;k++)
            {
                if( j == r[k])
                  update( dp2[k][r[k]] , dp2[i][j] );
                if( j < r[k] )
                  update( dp3[i][k][max(j,l[k] - 1)] , dp2[i][j]);
            }
        }
        ///dp3
        for(int j = i + 1;j <= n;j++)
        {
            for(int k = l[j] - 1;k <= r[j];k++)
            if(dp3[i][j][k] <= n)
            {
               // printf("dp3[%d][%d][%d] = %d\n",i,j,k,dp3[i][j][k]);
                if(j == i + 1)
                {
                    if(k < r[j])
                    {
                    //    printf("->dp1[%d][%d][%d]\n",i+1,min(k + 1,r[j]),r[j + 1]);
                        update(dp1[i + 1][min(k + 1,r[j])][r[j]] , dp3[i][j][k]);
                    }
                    else
                    {
                   //     printf("->dp2[%d][%d]\n",i+1,r[j]);
                        update(dp2[i + 1][r[j]] , dp3[i][j][k]);
                    }
                }
                else
                {
                    int nxtk = k;
                    if(r[i + 1] > k) nxtk++;
                    update(dp3[i + 1][j][nxtk],dp3[i][j][k] + 1);
                }
            }
        }
    }

    for(int i = 1;i <= n;i++)
    {
        for(int j = 0;j <= 100;j++)
        if(dp2[i][j] <= 100)
        {
         //   cout<<i<<" "<<j<<" "<<dp2[i][j]<<endl;
            update(ans , dp2[i][j] + n - i);
        }
    }

    printf("%d\n",ans);
}

int main()
{
    int Case,Res = 0;

    for (scanf("%d",&Case);Case > 0;Case--)
    {

        init();
        //printf("Case #%d: ",++Res);
        work();
    }

    return 0;
}

Stabilization

考虑去掉abs符号,发现只有相邻两个数的最高位被影响了才会影响abs的符号,所以可以按照最高位不一样的位置分类,之后考虑朴素枚举x从0到2^20,每次的复杂度是O(400),无法通过,考虑优化,第一种方法是用DFS来进行枚举,第二种则是加入记忆化

#include <cstdio>
#include <cassert>
#include <algorithm>
#include <iostream>
#include <ctime>
using namespace std;

long long cnt[20][20];
int mxi;
int d[20];
long long ans;
int x;

void dfs(int i, int cx, long long sum)
{
    if (i == mxi)
	{
		if (ans > sum || (ans == sum && x > cx))
			ans = sum, x = cx;
	}
	else
		for (d[i] = 0; d[i] < 2; ++d[i])
		{
			long long nsum = sum + cnt[i][i];
			for (int j = 0; j < i; ++j)
				if (d[i] ^ d[j])
					nsum -= cnt[i][j];
				else
					nsum += cnt[i][j];
			dfs(i + 1, cx | (d[i] << i),  nsum);
		}
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n;
		scanf("%d", &n);
		for (int i = 0; i < 20; ++i)
			for (int j = 0; j <= i; ++j)
				cnt[i][j] = 0;
		int a;
		scanf("%d", &a);
		for (int i = 1; i < n; ++i)
		{
			int b;
			scanf("%d", &b);
			int high = 19;
			while (high >= 0 && ~(a ^ b) >> high & 1)
				--high;
			int mx = max(a, b);
			int mi = min(a, b);
			for (int j = high; j >= 0; --j)
				cnt[high][j] += (mx >> j & 1) - (mi >> j & 1);
			a = b;
		}
		mxi = 20;
		while (mxi > 0 && !cnt[mxi - 1][mxi - 1])
			--mxi;
		for (int i = 0; i < 20; ++i)
			for (int j = 0; j <= i; ++j)
				cnt[i][j] <<= j;
		ans = 1e18;
		dfs(0, 0, 0);
		printf("%d %lld\n", x, ans);
	}
	return 0;
}

This world need more Zhu

卿学姐说她的fans都能过这个题【纯属放屁……

考虑链上的操作,我们用树上莫队来处理,因为很容易做到O(1)转移到相邻节点的状态。

考虑子树操作,可以启发式合并来维护答案,也可以先dfs序一下,然后再跑普通的莫队算法就好了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
typedef long long LL;
int n,m,a[maxn],b[maxn];
struct Edge
{
    int v,next;
}edge[maxn*2];
int head[maxn],ne;
inline void add(int u,int v)
{
	edge[ne].v=v;
	edge[ne].next=head[u];
	head[u]=ne++;
}
int idx,L[maxn],R[maxn],dep[maxn],fa[maxn],is[maxn];
int st[maxn],stn,pos[maxn],b_cnt;
const int sz=200;
int dfs(int u,int pre)
{

	L[u]=++idx;
	is[idx]=u;
	dep[u]=dep[pre]+1;
	fa[u]=pre;
	int siz=0;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].v;
		if(v==pre)continue;
		siz+=dfs(v,u);
		if(siz>sz)
		{
			while(siz--)pos[st[--stn]]=b_cnt;
			b_cnt++;
		}
	}
	st[stn++]=u;
	R[u]=idx;
	return siz+1;
}
struct QUERY
{
	int l,r,block,a,b,idx;
	QUERY(int L=0,int R=0,int A=0,int B=0,int id=0)
	{
		l=L;
		r=R;
		if(l>r)swap(l,r);
		block=L/sz;
		a=A;
		b=B;
		idx=id;
	}
	bool operator <(const QUERY &p)const
	{
		return block!=p.block?block<p.block:r>p.r;
	}
}qr1[maxn],qr2[maxn];
bool cmpd(const QUERY &a,const QUERY &b)
{
	return pos[a.l]<pos[b.l]||(pos[a.l]==pos[b.l]&&L[a.r]>L[b.r]);
}
int num[maxn],dis[maxn],dlen;
LL res[maxn],ans[maxn];
inline void add(int u)
{
	res[num[b[u]]]-=a[u];
	++num[b[u]];
	res[num[b[u]]]+=a[u];
}
inline void remove(int u)
{
	res[num[b[u]]]-=a[u];
	--num[b[u]];
	res[num[b[u]]]+=a[u];
}
bool in[maxn];
int cross;
inline void inv(int x) {
    if(in[x]) {
        in[x] = false;
        remove(x);
    }
    else {
        in[x] = true;
        add(x);
    }
}

//move x up
inline void move_up(int &x) {
    if(!cross) {
        if(in[x] && !in[fa[x]]) cross = x;
        else if(in[fa[x]] && !in[x]) cross = fa[x];
    }
    inv(x), x = fa[x];
}

//move a to b
void move(int a, int b) {
    if(a == b) return ;
    cross = 0;
    if(in[b]) cross = b;
    while(dep[a] > dep[b]) move_up(a);
    while(dep[b] > dep[a]) move_up(b);
    while(a != b) move_up(a), move_up(b);
    inv(a), inv(cross);
}
void read(int &x)
{
	char c;
	while(!isdigit(c=getchar()));
	x=c-'0';
	while(isdigit(c=getchar()))x=x*10+c-'0';
}
char s[35];
void print(LL x)
{
	int con=0;
	do
	{
		s[con++]=x%10+'0';
		x/=10;
	}while(x);
	for(int i=con-1;i>=0;--i)putchar(s[i]);
}
int main()
{
	/*
    int size = 256 << 20; // 256MB
    char *p = (char*)malloc(size) + size;
    __asm__("movl %0, %%esp\n" :: "r"(p));
    freopen("1.in","r",stdin);freopen("1.ans","w",stdout);*/
	int T,u,v;
	int op,l,r,A,B,q1,q2;
	scanf("%d",&T);
	for(int cas=1;cas<=T;++cas)
	{
		//cerr <<cas<<endl;
		ne=0;
		memset(head,-1,sizeof(head));
		//scanf("%d%d",&n,&m);
		read(n);read(m);
		for(int i=1;i<=n;++i)
		{
			//scanf("%d",&a[i]);
			read(a[i]);
			dis[i-1]=a[i];
		}
		sort(dis,dis+n);
		dlen=unique(dis,dis+n)-dis;
		for(int i=1;i<=n;++i)
			b[i]=lower_bound(dis,dis+dlen,a[i])-dis;
		//cout <<dlen<<endl;
		for(int i=2;i<=n;++i)
		{
			//scanf("%d%d",&u,&v);
			read(u);read(v);
			add(u,v);
			add(v,u);
		}
		idx=0;
		b_cnt=0;
		dfs(1,0);
		while(stn)pos[st[--stn]]=b_cnt;
		q1=q2=0;
		for(int i=0;i<m;++i)
		{
			//scanf("%d%d%d%d%d",&op,&u,&v,&A,&B);
			read(op);read(u);read(v);read(A);read(B);
			if(op==1)
				qr1[q1++]=QUERY(L[u],R[v],A,B,i);
			else
				qr2[q2++]=QUERY(u,v,A,B,i);
		}
		//for(int i=1;i<=n;++i)cout <<L[i]<<" "<<R[i]<<endl;
		//for(int i=1;i<=n;++i)cout <<dep[i]<<" "<<fa[i]<<endl;
		//for(int i=1;i<=n;++i)cout <<is[i]<<" is "<<a[is[i]]<<endl;
		sort(qr1,qr1+q1);
		memset(num,0,sizeof(num));
		memset(res,0,sizeof(res));
		for(int i=1;i<=n;++i)res[0]+=a[i];
		l=1,r=0;
		for(int i=0;i<q1;++i)
		{
			//cout <<qr1[i].l<<" "<<qr1[i].r<<endl;
			while(r<qr1[i].r)add(is[++r]);
			while(l<qr1[i].l)remove(is[l++]);
			while(r>qr1[i].r)remove(is[r--]);
			while(l>qr1[i].l)add(is[--l]);
			//for(int j=0;j<=n;++j)cout <<num[j]<<" ";cout <<endl;
			//for(int j=0;j<=n;++j)cout <<res[j]<<" ";cout <<endl;
			ans[qr1[i].idx]=__gcd(res[qr1[i].a],res[qr1[i].b]);
		}
		//cout <<"sequence"<<endl;
		sort(qr2,qr2+q2,cmpd);
		memset(num,0,sizeof(num));
		memset(res,0,sizeof(res));
		memset(in,0,sizeof(in));
		for(int i=1;i<=n;++i)res[0]+=a[i];
		l=r=1;
		add(1);
		in[1]=true;
		for(int i=0;i<q2;++i)
		{
			move(l,qr2[i].l);
			move(r,qr2[i].r);
			l=qr2[i].l;
			r=qr2[i].r;
			/*cout <<qr2[i].l<<" "<<qr2[i].r<<" "<<l<<" "<<r<<endl;
			for(int j=0;j<=n;++j)cout <<num[j]<<" ";cout <<endl;
			for(int j=0;j<=n;++j)cout <<res[j]<<" ";cout <<endl;*/
			ans[qr2[i].idx]=__gcd(res[qr2[i].a],res[qr2[i].b]);
		}
		printf("Case #%d:\n",cas);
		for(int i=0;i<m;++i)
		{
			print(ans[i]);puts("");
			//printf("%I64d\n",ans[i]);
		}
	}
	return 0;
}

To My Girlfriend

令dp[i][j][s1][s2]表示前i个物品填了j的体积,有s1个物品选为为必选,s2个物品选为必不选的方案数(0<=s1,s2<=2),则有转移方程dp[i][j][s1][s2] = dp[i - 1][j][s1][s2] + dp[i - 1][j - a[i]][s1 - 1][s2] + dp[i - 1][j][s1][s2 - 1],边界条件为dp[0][0][0][0] = 1,时间复杂度O(NS*3^2)。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1005;
typedef long long LL;
const LL MOD = 1e9 + 7;

int n,s;
LL dp[2][N][3][3];
LL ans = 0;
int a[N];

void init()
{
    scanf("%d %d",&n,&s);
    for(int i = 1;i <= n;i++)
      scanf("%d",&a[i]);
}

void update(LL &_x,LL _y)
{
    _x = _x + _y;
    _x = _x > MOD ? _x - MOD : _x;
}

void work()
{
    memset(dp, 0, sizeof(dp));
    ans = 0;
    dp[0][0][0][0] = 1;
    bool sign = false;

    for(int i = 1;i <= n;i++)
    {
        sign = !sign;
        for(int j = s;j >= 0;j--)
        {
            for(int s1 = 0;s1 < 3;s1++)
              for(int s2 = 0;s2 < 3;s2++)
              {
                  dp[sign][j][s1][s2] = dp[!sign][j][s1][s2] ;
                  if(j >= a[i]) update(dp[sign][j][s1][s2] , dp[!sign][j - a[i]][s1][s2]);

                  if(s1 > 0 && j >= a[i])
                  {
                      update(dp[sign][j][s1][s2] , dp[!sign][j - a[i]][s1 - 1][s2]);
                  }
                  if(s2 > 0)
                  {
                      update(dp[sign][j][s1][s2] , dp[!sign][j][s1][s2 - 1]);
                  }
                //  if(dp[sign][j][s1][s2] > 0)
                //  {
               //       printf("dp[%d][%d][%d][%d] = %I64d\n",i,j,s1,s2,dp[sign][j][s1][s2]);
               //   }
              }
        }
    }

    for(int i = 1;i <= s;i++)
    {
        update(ans , dp[sign][i][2][2]);
    }
    ans = (ans * 2 % MOD ) * 2 % MOD;
    cout<<ans<<endl;
}

int main()
{
    int T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;++cas)
    {
        //cerr <<cas<<endl;
        init();
        work();
    }
    return 0;
}

Up Sky,Mr.Zhu

我们把所有长度相同的回文串插入到同一个可持久化字典树里面

然后

在线查询[L,R] 特征串为T 长度为1 的回文串的数量
在线查询[L+1,R] 特征串为T 长度为2 的回文串的数量
在线查询[L+2,R] 特征串为T 长度为3 的回文串的数量
......
在线查询[L+9,R] 特征串为T 长度为10 的回文串的数量

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

const int maxn=1e5+100;
char s[maxn],t[50];
int nxt[maxn*20][5],num[maxn*20];
int tr[20][maxn],sz;
void update(int &tr,int pre,char buf[],int pos)
{
    tr=++sz;
    num[tr]=num[pre]+1;
    for(int i=0;i<5;++i)
        nxt[tr][i]=nxt[pre][i];
    if(buf[pos]=='\0')return;
    update(nxt[tr][buf[pos]-'a'],nxt[pre][buf[pos]-'a'],buf,pos+1);
}
int query(int lch,int rch,char buf[],int pos)
{
    if(buf[pos]=='\0')
        return num[rch]-num[lch];
    return query(nxt[lch][buf[pos]-'a'],nxt[rch][buf[pos]-'a'],buf,pos+1);
}
bool is(int i)
{
    for(int p=0,q=i-1;p<q;++p,--q)
        if(t[p]!=t[q])return false;
    return true;
}
int main()
{
    int cas=1;
    while(scanf("%s",s+1)!=EOF)    
    {
        sz=0;
        memset(tr,0,sizeof(tr));
        int n=strlen(s+1);
        for(int i=1;i<=20;++i)
            for(int j=i,k=1;j<=n;++j,++k)
            {
                for(int p=k,q=0;p<=j;++p,++q)
                    t[q]=s[p];
                t[i]='\0';
                if(is(i))
                {
                    //cout <<k<<" "<<j<<" "<<t<<" "<<i/2<<endl;
                    update(tr[i-1][j],tr[i-1][j-1],t,i/2);
                }
                else tr[i-1][j]=tr[i-1][j-1];
            }
        int q,l,r;
        scanf("%d",&q);
    //    printf("Case #%d:\n",cas++);
        for(int i=0;i<q;++i)
        {
            scanf("%d%d%s",&l,&r,t);
            //if(l>r)cerr <<"should swap"<<endl;
            //if(r<=0||r>n||l<=0||l>n)cerr <<i<<" j&r limit error:"<<l<<" "<<r<<endl;
            int res=0;
            for(int i=0,j=l-1;i<20&&j<r;++i,++j)
            {
                res+=query(tr[i][j],tr[i][r],t,0);
                //cout <<"length:"<<i+1<<" "<<res<<endl;
                //cout <<query(0,tr[i][j],t,0)<<" "<<query(0,tr[i][r],t,0)<<endl;
            }
            printf("%d\n",res);
        }
    }
    return 0;
}

Windows 10

您可能是正版Windows 10的受害者
直接贪心就好

比较直观的看法是使劲往下降,然后升回来

或者使劲往下降然后停顿然后再使劲往下降。。。

于是就能将问题变成一个子问题,然后dfs就好

需要注意的是由于按up键也可以打断连续向下的功效

所以应该记录停顿了几次,以后向上的时候用停顿补回来

#include <cstdio>
#include <algorithm>

int T;
long long P, Q;

long long dfs(long long S, long long T, long long step, long long stop) {
    //printf("%lld %lld %lld %lld\n", S, T, step, stop);
	if (S == T) return step;
	long long x = 0;
	while (S - (1 << x) + 1 > T) x++;
	if (S - (1 << x) + 1 == T) return step + x;
	long long up = (T - std::max((long long)0, S - (1 << x) + 1));
	long long tmp = x + std::max((long long)0, up - stop);
	return std::min(tmp + step, dfs(S - (1 << (x - 1)) + 1, T, step + x, stop + 1));
}

int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%lld%lld", &P, &Q);
		if (Q >= P) {
			printf("%lld\n", Q - P);
			continue;
		}
		printf("%lld\n", dfs(P, Q, 0, 0));
	}
	return 0;
}

Zhu’s Math Problem

方法1 :暴力小数据,暴力解方程,即假设答案是关于A,B,C,D的小于等于k次的多项式方程,然后解即可

方法2 :分类讨论,比较烦

方法3 :考虑数位DP,从高位到低位依次放数字即可,加上状态表示a+c-b-d,a+d-b-c即可

#include <bits/stdc++.h>

using namespace std;
const int mod = 1e9 + 7;
int dp[65][1 << 4][4][4],ha[3]={-2,0,2};
long long p[4];
inline void Modify( int & x , int v ){ x += v ; if ( x >= mod ) x -= mod ;}
inline int Transform( int a , int f ){ if( f == 3 ) return 3; int rs = a + ha[f] ; if( rs < -1 ) return -1 ; if( rs > 1 ) return 3 ; return rs + 1;}

int dfs( int x , int mask , int f1 , int f2 ){
    if( x == -1 ) return f1 >= 2 && f2 >= 1;
    if( ~ dp[x][mask][f1][f2] ) return dp[x][mask][f1][f2];
    int ed[4] , & ans = dp[x][mask][f1][f2] = 0;
    for(int i = 0 ; i < 4 ; ++ i) ed[i] = mask >> i & 1 ? 1 : (p[i] >> x & 1);
    for(int i = 0 ; i <= ed[0] ; ++ i)
        for(int j = 0 ; j <= ed[1] ; ++ j)
            for(int k = 0 ; k <= ed[2] ; ++ k)
                for(int v = 0 ; v <= ed[3] ; ++ v){
                    int newf1 = Transform( i + k - j - v , f1 ) , newf2 = Transform( i + v - j - k , f2 ) , newmask = mask ;
                    if( newf1 == -1 || newf2 == -1 ) continue;
                    if( i < ed[0] ) newmask |= 1;
                    if( j < ed[1] ) newmask |= 2;
                    if( k < ed[2] ) newmask |= 4;
                    if( v < ed[3] ) newmask |= 8;
                    Modify( ans , dfs( x - 1 , newmask , newf1 , newf2 ) );
                }
    return ans;
}

int main( int argc , char * argv[] ){
    int T ; scanf("%d",&T);
    while(T--){
        memset( dp , -1 , sizeof( dp ) );
        scanf("%I64d%I64d%I64d%I64d" , & p[0] , & p[1] , & p[2] , & p[3] );
        printf( "%d\n" , dfs( 61 , 0 , 1 , 1  ) );
    }
    return 0;
}

ps:如果对本场题目质量有什么意见,请猛戳这个传送门,欢迎大家前来吐槽

posted @ 2016-08-04 17:02  qscqesze  阅读(2184)  评论(18编辑  收藏  举报