2016 Multi-University Training Contest 6 题解
我只能说:
A Boring Question
下面公式重复了一行
#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:如果对本场题目质量有什么意见,请猛戳这个传送门,欢迎大家前来吐槽