给你一颗树,然后A点在1号点,所有点都有人,每一秒A和所有人都可以移动一格,A可以往树下走,其他人往A方向走,A与人相遇只有在点上或边上遇见才叫相遇,相遇后人消失,问A走到每个点碰到的人,n <=10^5; ai <= 1000
首先A走下来,有些人是永远碰不到A的,假设A的一开始的深度为0,A去到一个点x,那么一个点碰到A点当且仅当 这个点i的(dep[i]+1)/2的祖先在A到x的路径上
然后对于每个点倍增统计到那个点上,然后搜一遍下来就好了
#include
using namespace std;
const int N = 200010;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node{int x,y,next;}edge[N]; int len,first[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
int n,a[N],b[N];
int dep[N],fa[N][25];
void dfs(int x,int f)
{
for(int k=first[x];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
if(y==f) continue;
dep[y]=dep[x]+1; fa[y][0]=x; dfs(y,x);
}
}
int ans[N];
void dfs2(int x,int f,int s)
{
ans[x] = s + b[x];
for(int k=first[x];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
if(y==f) continue;
dfs2(y,x,s+b[x]);
}
}
int main()
{
freopen("escape.in","r",stdin);
freopen("escape.out","w",stdout);
n=read(); for(int i=1;i<=n;i++) a[i]=read(); len=0; memset(first,-1,sizeof(first));
for(int i=1;iint x=read(); int y=read(); ins(x,y); ins(y,x);}
dep[1]=0; dfs(1,0);
for(int j=1;j<=20;j++) for(int i=1;i<=n;i++) fa[i][j] = fa[fa[i][j-1]][j-1];
for(int i=1;i<=n;i++)
{
int deep = dep[i] - (dep[i]+1)/2; int x=i;
for(int j=20;j>=0;j--) if(deep >= (1<x=fa[x][j],deep-=(1<x] += a[i]; // printf("%d %d\n",i,x);
}
dfs2(1,0,0);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
有
看到998244353就先写个NTT
首先我们很快的分析一波性质
后面显然是和1进行狄利克雷卷积,1卷积后的狄利克雷卷积满足交换律结合律,同时也是一个积性函数
第一个想法,把1的狄利克雷卷积给倍增,然后log提出来,乘的时候要nlogn的时间,总的时间复杂度应该是
q=1 | q=2 | q=3 | q=4 | q=5 | |
---|---|---|---|---|---|
k=1 | 1 | 1 | 1 | 1 | 1 |
k=2 | 1 | 2 | 3 | 4 | 5 |
k=3 | 1 | 3 | 6 | 10 | 20 |
其实就是只和q有关,和p没有关系,这样每一行的每一个,就是上面的前缀和
还有一个规律就是,每一项都是左边+上面,这样让我们想起了一个东西
对就是组合数,总结一下规律,就有表中的g[k][q]
然后我们就可以用g求出f了
过程有点繁琐,但是熟练的选手还是可以很快出来的
预处理组合数即可,时间复杂度
#include
#define ll long long
#define C(i,j) (fac[(i)] * inv[(j)] % mod * inv[(i)-(j)] % mod)
using namespace std;
const ll N = 200010;
const ll mod = 998244353;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll jcp[N][20]; ll a[N];
ll fac[N],inv[N];
bool v[N]; ll prime[N],pri=0,minx[N];
void get_prime(ll n)
{
memset(v,1,sizeof(v)); v[0] = v[1] = 0; pri=0;
for(ll i=2;i<=n;i++)
{
if(v[i]) prime[++pri] = i,minx[i] = i;
for(ll j=1;(j<=pri) && (i*prime[j]<=n);j++)
{
v[i*prime[j]]=0; minx[i*prime[j]] = prime[j];
if(i%prime[j]==0) break;
}
}
}
ll calc(ll k,ll x)
{
ll s=1; ll lst=minx[x]; ll p=0;
while(x!=1)
{
while(minx[x] == lst && x!=1) p++,x=x/minx[x];
s = s * C(p+k-1,p) % mod;
if(x!=1) lst=minx[x],p=0;
}
return s;
}
int main()
{
freopen("machine.in","r",stdin);
freopen("machine.out","w",stdout);
ll n = read(); get_prime(2e5);
for(ll i=1;i<=n;i++) a[i] = read();
fac[0] = 1; for(ll i=1;i<=(ll)2e5;i++) fac[i] = fac[i-1] * i % mod;
inv[0] = inv[1] = 1; for(ll i=2;i<=(ll)2e5;i++) inv[i] = (mod - mod/i) * inv[mod%i] % mod;
for(ll i=1;i<=(ll)2e5;i++) inv[i] = inv[i-1] * inv[i] % mod;
// printf("%lld\n",calc(3,1000));
ll q = read();
while(q--)
{
ll k=read(); ll x=read(); ll ans = 0;
for(ll i=1;i*i<=x;i++) if(x%i==0)
{
if(i*i==x)
{
ans = (ans + a[i] * calc(k,i) % mod) % mod;
}
else
{
ans = (ans + a[i] * calc(k,x/i) % mod) % mod;
ans = (ans + a[x/i] * calc(k,i) % mod) % mod;
/*printf("%lld %lld %lld\n",k,x/i,a[i]);
printf("%lld %lld %lld\n",k,i,a[x/i]);*/
}
}
printf("%lld\n",ans);
}
return 0;
}
这是一道交互题
给你一颗二叉树,每个点连着3条边,每条边的颜色分别是0,1,2,根是出口
你可以调用两个函数
int query() 表示到出口的距离
void move(x)表示从当前点走颜色为x的边
你要走到出口,且询问次数在T之内,移动次数小于10^6次方
让你完成一个函数
void findpath(int initialDeep, int T);
initalDeep表示初始深度
对于100% 的数据满足initialDeep <=10^5; T >= 51000
我们先找一个naive的做法,记录从哪条边上来,这条边就不可能是了,最初的点试2次,然后之后每个点平均要试1.5次,就可以得到T>=150000的分
考虑再优化,我们肯定是随便走(当然从哪条边走上来的肯定那条边就不用管),走一段路再询问一遍,每段路径假设走两步,这样的路径肯定是往上走一段,然后走下来,当然往上走一段和往下走一段的长度可能为0
每次最坏情况下是走2步,只上去1步
还有就是走2步,上去2步,平均询问一遍,上去1.5步
然后我们再考虑一下,把这种情况变成走多步就好了,我走了6步,很容易得出期望步数大约为2
#include
#include "maze.h"
#define MOVE(x) if(move(x)) return ;
#define qry() query()
int col(int c1,int c2)
{
if(c1 > c2){int t=c1; c1=c2; c2=t;}
if(c1==0 && c2==1) return 2;
if(c1==0 && c2==2) return 1;
if(c1==1 && c2==2) return 0;
}
int col_rand(int c)
{
// srand(time(0));
int op=rand()%2;
int a,b; if(c==0) a=1,b=2;
else if(c==1) a=0,b=2;
else if(c==2) a=0,b=1;
if(op==0) return a;
else return b;
}
int a[10],alen=0;
void findpath(int initialDeep, int T)
{
int dep = initialDeep; int sl,c;
MOVE(0); int c1 = qry(); MOVE(0);
MOVE(1); int c2 = qry(); MOVE(1);
if(c1 == dep-1) sl = 0;
else if(c2 == dep-1) sl = 1;
else sl = 2;
MOVE(sl); dep--; //printf("%d\n",qry());
while(1)
{
alen = 0; a[alen] = sl;
for(int i=1;i<=6;i++)
{
a[i] = col_rand(a[i-1]);
MOVE(a[i]);
}
int nowDep = qry();
int deep = nowDep - dep;
if(deep == 6)
{
for(int i=6;i>=1;i--){MOVE(a[i]) }
sl = col(sl,a[1]);
MOVE(sl)
dep = dep - 1;
}
else if(deep == 4)
{
for(int i=6;i>=2;i--){MOVE(a[i]) }
sl = col(a[1],a[2]);
MOVE(sl)
dep = dep - 2;
}
else if(deep == 2)
{
for(int i=6;i>=3;i--){MOVE(a[i]) }
sl = col(a[2],a[3]);
MOVE(sl)
dep = dep - 3;
}
else if(deep == 0)
{
for(int i=6;i>=4;i--){MOVE(a[i]) }
sl = col(a[3],a[4]);
MOVE(sl)
dep = dep - 4;
}
else if(deep == -2)
{
for(int i=6;i>=5;i--){MOVE(a[i]) }
sl = col(a[4],a[5]);
MOVE(sl)
dep = dep - 5;
}
else if(deep == -4)
{
for(int i=6;i>=6;i--){MOVE(a[i]) }
sl = col(a[5],a[6]);
MOVE(sl)
dep = dep - 6;
}
else sl = a[6],dep = dep - 6;
}
}
给出一个长度为m 的序列A, 请你求出有多少种1…n 的排列, 满足A 是它的一个LIS.
这道题做了几遍了,我们把这些数给压缩一下,考虑之前LIS是怎么求的,开一个栈,如果一个元素在栈内,可能会被后面替换掉,那么在栈中的元素就是1,在栈外的就是2,没入栈的是0,然后用bfs来dp一遍就好了
题目中有一个固定的上升子序列,那么就按顺序插入,然后状态是答案,当且仅当这个状态在栈中的元素的个数是原题题目LIS的长度,还有所有元素要不在栈中,要不在栈外
#include
#define ll long long
using namespace std;
const ll N = 17;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll n,k,a[N],pre[N],bin[N]; ll f[15000000];
queue q; ll nex[N];
int main()
{
freopen("arg.in","r",stdin);
freopen("arg.out","w",stdout);
n=read(); k=read(); for(ll i=0;i1;
ll lst=read()-1; for(ll i=2;i<=k;i++){ll x=read()-1; pre[x] = lst; lst=x;}
q.push(0); memset(f,0,sizeof(f)); f[0] = 1;
bin[0] = 1; for(ll i=1;i<=n;i++) bin[i] = bin[i-1] * 3; ll ans = 0;
while(!q.empty())
{
ll x=q.front();
nex[n]=n; for(ll i=n-1;i>=0;i--) if(x/bin[i]%3 == 1) nex[i] = i; else nex[i] = nex[i+1];
ll s=0; for(ll i=0;iif(x/bin[i]%3 == 1) s++;
ll cnt=0; for(ll i=0;iif(x/bin[i]%3) cnt++;
if((x/bin[lst]%3) && (s==k) && (cnt==n)) ans+=f[x];
for(ll i=0;i//Add
{
ll ss=s; ll p = x/bin[i]%3; if(p) continue;
if(pre[i]!=-1)
{
ll q = x/bin[pre[i]]%3;
if(!q) continue;
}
ll sta = x + bin[i];
if(nex[i]else ss++;
if(ss<=k)
{
// printf("%lld -> %lld\n",x,sta);
if(!f[sta]) q.push(sta);
f[sta]+=f[x];
}
}q.pop();
}
return printf("%lld\n",ans),0;
}
给定一个正n 边形及其三角剖分, 共2n-3 条边(n 条多边形的边和n-3 条对角线), 每条边的长度为1.
共q 次询问, 每次询问给定两个点, 求它们的最短距离.
我们可以用分治写这道题,维护每次到分治端点的距离,但是考虑到太恶心,我太菜了,就没写,口服一番
有一个n*m 的地图, 地图上的每一个位置可以是空地, 炮塔或是敌人. 你需要操纵炮塔消灭敌人.
对于每个炮塔都有一个它可以瞄准的方向, 你需要在它的瞄准方向上确定一个它的攻击位置,
当然也可以不进行攻击. 一旦一个位置被攻击, 则在这个位置上的所有敌人都会被消灭.
保证对于任意一个炮塔, 它所有可能的攻击位置上不存在另外一个炮塔.
定义炮弹的运行轨迹为炮弹的起点和终点覆盖的区域. 你需要求出一种方案, 使得没有两条炮弹轨迹相交.
一般这种题我们考虑用流来做
因为射出来是一条线段,我们考虑用链构图,然后用最小割,割掉的边代表要选
对于攻击方向上下的炮台,我们这样建
源点->炮台->射出的第一个位置->射出的第二个位置….->汇点
其中位置前的边权为这个位置的数字,但是因为这样是割掉最小的,我们要最大的,就套路一波把数字变成一个大数-这个数字,我用的是1e8-a[i][j]
因为我们有限制,两条路径不能交叉,而且一行之内的炮台是互不干扰的,因为题目所说,炮台不会出现在别人的攻击范围之内,我们考虑如何限制,这也是这道题为什么用最小割比较好的原因。
我们把左右的炮台反着建,然后连边就好了
源点->射出的最后一个位置->射出的倒数第二个位置->…->炮台->汇点
然后对应相交的地方连边
这样是为什么呢,如果两个都割射出位置比较远的点,必然这个图还联通,自己画画就好
#include
#define ll long long
#define pb push_back
using namespace std;
const ll N = 1000010;
const ll inf = 1e13;
inline ll read()
{
char ch=getchar(); ll p=0; ll f=1;
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node{ll x,y,c,next;}edge[N]; ll len,first[N];
void ins(ll x,ll y,ll c)
{
len++; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
len++; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
//printf("%lld %lld %lld\n",x,y,c);
}
ll dep[N]; queue q; ll s,d;
bool bfs()
{
memset(dep,0,sizeof(dep)); dep[s] = 1;
while(!q.empty()) q.pop(); q.push(s);
while(!q.empty())
{
ll x=q.front();
for(ll k=first[x];k!=-1;k=edge[k].next)
{
ll y = edge[k].y;
if(dep[y]==0 && edge[k].c)
{
dep[y] = dep[x] + 1;
q.push(y);
}
}
q.pop();
}
return dep[d] > 0;
}
ll dfs(ll x,ll flow)
{
if(x==d) return flow;
ll delta = 0;
for(ll k=first[x];k!=-1;k=edge[k].next)
{
ll y = edge[k].y;
if(flow > delta && dep[y] == dep[x]+1 && edge[k].c)
{
ll minf = dfs(y,min(flow-delta,edge[k].c));
edge[k].c-=minf; edge[k^1].c+=minf;
delta+=minf;
}
}
if(delta==0) dep[x]=0;
return delta;
}
ll a[110][110];
vector v[110][110];
int main()
{
freopen("cti.in","r",stdin);
freopen("cti.out","w",stdout);
len=1; memset(first,-1,sizeof(first)); ll tot = 0;
ll n = read(); ll m = read();
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) a[i][j] = read(); s=1; d=2; tot=2;
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) if(a[i][j] <= -1 && a[i][j] >= -2)
{
tot++; ins(tot,d,1e8);
if(a[i][j] == -1)
{
for(ll k=i-1;k>=1;k--)
{
v[k][j].pb(tot); tot++; ins(tot,tot-1,1e8-a[k][j]);
}
ins(s,tot,inf); //printf("...\n");
}
else
{
for(ll k=i+1;k<=n;k++)
{
v[k][j].pb(tot); tot++; ins(tot,tot-1,1e8-a[k][j]);
}
ins(s,tot,inf); //printf("....\n");
}
}
for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) if(a[i][j] <= -3 && a[i][j] >= -4)
{
tot++; ins(s,tot,1e8);
if(a[i][j] == -3)
{
for(ll k=j-1;k>=1;k--)
{
for(ll l=0;l1,tot,1e8-a[i][k]);
}
ins(tot,d,inf); //printf(".\n");
}
else
{
for(ll k=j+1;k<=m;k++)
{
for(ll l=0;l1,tot,1e8-a[i][k]);
}
ins(tot,d,inf); //printf("..\n");
}
}
ll ans = 0;
while(bfs()) ans-=dfs(s,inf);
while(ans < 0) ans+=1e8;
return printf("%lld\n",ans),0;
}
两个字符串S和T,然后T在S中匹配当且仅当T是S的子串并且T在S中的对应位置转化相同
也就是
3 1 3与1 2 1匹配
字符集大小为c
n,m,c<=10^6
这道题好像之前见过类似的,只要找到与前一个位置的距离
可能与前面的一个位置的距离大于当前串的距离,这个位置就变成没有出现过的了,可以用kmp或者hash来匹配
#include
#define pb push_back
using namespace std;
const int N = 1000010;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
int a[N],b[N],wa[N],wb[N];
int cnt[N];
int fail[N],nx[N];
vector<int>ans;
int main()
{
freopen("xiz.in","r",stdin);
freopen("xiz.out","w",stdout);
int t=read(); int s=read();
while(t--)
{
int n = read(); int m = read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++) b[i]=read();
for(int i=1;i<=s;i++) cnt[i] = 0;
for(int i=1;i<=n;i++)
{
if(!cnt[a[i]]) wa[i] = 0;
else wa[i] = i - cnt[a[i]];
cnt[a[i]]=i;
}
for(int i=1;i<=s;i++) cnt[i] = 0;
for(int i=1;i<=m;i++)
{
if(!cnt[b[i]]) wb[i] = 0;
else wb[i] = i - cnt[b[i]];
cnt[b[i]]=i;
}
for(int i=1;i<=n;i++) fail[i] = nx[i] = 0;
int p=0; fail[1] = 0; int c,d;
for(int i=2;i<=m;i++)
{
while(p)
{
if(p+1>m) p=fail[p];
c = wb[p+1]; d = wb[i];
if(d>p) d=0;
if(c==d) break;
p=fail[p];
}
c = wb[p+1]; d = wb[i];
if(d>p) d=0;
if(c==d) p++;
fail[i] = p;
}
p=0;
for(int i=1;i<=n;i++)
{
while(p)
{
if(p+1>m) p=fail[p];
c = wb[p+1]; d = wa[i];
if(d>p) d=0;
if(c==d) break;
p=fail[p];
}
c = wb[p+1]; d = wa[i];
if(d>p) d=0;
if(c==d) p++;
nx[i] = p;
}
ans.clear(); for(int i=1;i<=n;i++) if(nx[i] == m) ans.pb(i-m+1);
printf("%d\n",ans.size());
for(int i=0;iprintf("%d%c",ans[i]," \n"[i==ans.size()-1]);
}
return 0;
}
在平面上找n 个点, 要求这n 个点离原点的距离分别为r1; r2, rn. 最大化这n 个点构成的
凸包面积, 凸包上的点的顺序任意.
枚举点的顺序,对于角度,我们设一设,有
于是对于两个函数求偏导,就有了
#include
#define pb push_back
using namespace std;
const int N = 10;
const int inf = 1e6;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
double r[N]; int n; double pi = acos(-1);
int a[N]; bool v[N]; double maxx=0.0;
bool check(double lambda,int k)
{
double ans = 0.0;
for(int i=1;idouble theta = 2*lambda / r[a[i]] / r[(i+1) >= k ? a[1] : a[(i+1)]];
if(theta > 1) return 1;
if(theta < -1) return 0;
ans += acos(theta);
}return ans<=(2*pi);
}
void dfs(int k)
{
if(k>=4)
{
// for(int i=1;i<=n;i++) printf("%d ",a[i]); printf("\n");
double lambda = 0;
double L = -inf; double R = inf;
while(R-L>=1e-3)
{
double mid = (L+R)/2.0;
if(check(mid,k)) R=mid,lambda=mid;
else L=mid;
}
//cout<
double ans=0.0;
for(int i=1;idouble theta = acos(lambda * 2 / r[a[i]] / r[(i+1) >= k ? a[1] : a[(i+1)]]);
ans += r[a[i]] * r[(i+1) >= k ? a[1] : a[(i+1)]] / 2 * sin(theta);
}
maxx = max(maxx , ans);
// for(int i=1;i
//
if(k==n+1) return ;
}
for(int i=1;i<=n;i++) if(!v[i]){a[k] = i; v[i] = 1; dfs(k+1); v[i] = 0;}
}
int main()
{
freopen("yja.in","r",stdin);
freopen("yja.out","w",stdout);
// printf("%.6f\n",acos(1.1));
n=read(); for(int i=1;i<=n;i++) r[i] = read();
memset(v,0,sizeof(v)); dfs(1);
return printf("%.10f\n",(double)maxx),0;
}
给定m元的不等式
对于30%的数据
这里只给30分的做法
我们可以考虑容斥原理,枚举有i个强制选t,枚举答案,于是有
给你1到n的排列ai,bi,ci
三元组(x,y,z)合法,当且仅当存在一个下标集合S是[n]的子集满足
我们只要考虑,对于这样的一个三元组,只要选出1,2,3列,就可以对应一个三元组
选出1列的直接就是有n个可能的三元组
选出2列的就要减去a,b,c都在同一列的,就可以用一个CDQ三维数点来维护
选出3列的直接算不好算,我们考虑间接算
记A集合是选出3列中,其中有一列a,b,c都是最大的方案数,也是可以通过之前的CDQ处理
记B集合是选出3列中,其中有一列在a,b,c中任选两个是最大的方案数
这个还不好求,因为有a,b,c,我们试图也间接求这个东西
假设最大的是a,b,我们先不看c,找到三列中a,b都是最大的,c的关系我们忽略,同样的,忽略a,b算一次,我们记这个总的方案数是X
再确定c的关系,除去A集合的剩下的都是B集合的
有
#include
#define ll long long
using namespace std;
const ll N = 100010;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node
{
ll a,b,c,d;
}q[N]; ll cnt[N]; ll n;
bool cmpa(const node &x,const node &y){return x.a<y.a;}
bool cmpb(const node &x,const node &y){return x.b<y.b;}
bool cmpc(const node &x,const node &y){return x.c<y.c;}
ll tr[N];
ll low_bit(ll x){return x&(-x);}
void add(ll x,ll c){while(x<=n){tr[x]+=c; x+=low_bit(x);}}
ll qry(ll x){ll s=0; while(x>=1){s+=tr[x]; x-=low_bit(x);} return s;}
void CDQ(ll L,ll R)
{
if(L==R){return ;}
ll mid=(L+R)>>1;
CDQ(L,mid); CDQ(mid+1,R);
sort(q+L,q+mid+1,cmpb); sort(q+mid+1,q+R+1,cmpb);
ll j = L;
for(ll i=mid+1;i<=R;i++)
{
while(q[j].b<=q[i].b && j<=mid){add(q[j].c,1); j++;}
cnt[q[i].d] += qry(q[i].c);
}
while(j>L){j--; add(q[j].c,-1);}
}
int main()
{
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
n = read();
for(ll i=1;i<=n;i++) q[i].a=read(),q[i].d=i;
for(ll i=1;i<=n;i++) q[i].b=read();
for(ll i=1;i<=n;i++) q[i].c=read();
sort(q+1,q+n+1,cmpa);
CDQ(1,n);
ll ans = n;
ans += n*(n-1)/2; for(ll i=1;i<=n;i++) ans-=cnt[i];
ans += n*(n-1)*(n-2)/6;
ll A = 0; for(ll i=1;i<=n;i++) if(cnt[i]>=2) A+=cnt[i] * (cnt[i]-1) / 2;
ll X = 0;
sort(q+1,q+n+1,cmpa); memset(tr,0,sizeof(tr));
for(ll i=1;i<=n;i++)
{
ll s = qry(q[i].b);
X+=s*(s-1)/2; add(q[i].b,1);
}
sort(q+1,q+n+1,cmpa); memset(tr,0,sizeof(tr));
for(ll i=1;i<=n;i++)
{
ll s = qry(q[i].c);
X+=s*(s-1)/2; add(q[i].c,1);
}
sort(q+1,q+n+1,cmpb); memset(tr,0,sizeof(tr));
for(ll i=1;i<=n;i++)
{
ll s = qry(q[i].c);
X+=s*(s-1)/2; add(q[i].c,1);
}
ll B = X - 3*A;
ans -= A + B;
return printf("%lld\n",ans),0;
}
给你一颗有n 个点的树,其中1 号点为根节点,每个点都有一个权值val[i]
你可以从树中选择一些点,注意如果i 与j 都被选中且j 在i 的子树内,那么必须满足val[i]>val[j]
请你求出最多能同时选出多少个点
这是一道树上的LIS问题
一开始状态的定义是dp[i][0]表示这个结点选不选,发现这样很难转移与优化,我们考虑换个状态
因为子树内选不选和大小有关系,考虑状态dp[i][j]表示i结点子树下面最大值为j
这样的话你维护上去就维护一个当前点的值val[i],你找到一个val[i] - 1的前缀最大值,然后对于后面val[i] - n,你都把这个最大值+1用永久化标记放上去max一下,而子树互不干扰直接+上去
就是要线段树合并还有标记永久化,但是我不会
这里给出一个美妙的做法
在树上的LIS和普通的LIS,也是用一个数组维护,然后二分找到一个>=val[i]的替换掉
在树上也是同理,每个点用一个set维护一些权值,假设一个权值为x
那么如果这个权值x在set中排名为k,那么代表选x有k个贡献
于是像第一种做法一样,子树互不干扰的+操作相当于启发式合并set,然后看看当前结点的权值在set中找到最大的小于等于的替换掉,替换掉不改变后面的排名,这样就可以了。
#include
using namespace std;
const int N = 200010;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node{int x,y,next;}edge[N<<1]; int len,first[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
int n,val[N]; multiset<int>s[N];
void merge(int x,int y)
{
if(s[x].size() < s[y].size()) swap(s[x],s[y]);
multiset<int> :: iterator it;
for(it = s[y].begin();it != s[y].end();++it) s[x].insert(*it);
s[y].clear();
}
void dfs(int x)
{
for(int k=first[x];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
dfs(y); merge(x,y);
}
multiset<int> :: iterator it = s[x].lower_bound(val[x]);
if(it != s[x].end()) s[x].erase(it);
s[x].insert(val[x]);
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(); len=0; memset(first,-1,sizeof(first));
for(int i=1;i<=n;i++){val[i] = read(); int f=read(); if(f) ins(f,i);}
dfs(1);
return printf("%d\n",s[1].size()),0;
}
给你一个长度为 n n 的序列A,从1开始标记,全为0,进行 m m 次操作,对于第i次操作,你可以选一个二元组 (j,k) ( j , k ) , j∈[1,n],k∈[0,c] j ∈ [ 1 , n ] , k ∈ [ 0 , c ] ,并让 Aj=Aj+k A j = A j + k ,其中选定二元组 (j,k) ( j , k ) 的概率是 Pi,j,k P i , j , k
询问m次操作后序列最大值的期望 mod109+7 m o d 10 9 + 7
我们不能从操作那里入手枚举操作来dp,这样好像没办法记录到之前的序列A的状况,不妨我们枚举序列A中的每一位
设 f[i][mask][k] f [ i ] [ m a s k ] [ k ] 表示序列的前i位,已经用了mask个状态的操作,然后最大值为k的概率,我们如果要求期望,可以直接乘上k即可
这样考虑转移需要什么,我们需要知道mask状态加到序列第i位的概率
于是就有了另外一个状态 g[i][mask][k] g [ i ] [ m a s k ] [ k ] 表示第i位用mask的操作最大值为k
考虑转移的时间,加起来是 O(n2m(mc)2+n3m(mc)2) O ( n 2 m ( m c ) 2 + n 3 m ( m c ) 2 )
#include
#define ll long long
#define bin(i) (1<<(i))
using namespace std;
const ll N = 41;
const ll Mod = 1e9 + 7;
inline ll read()
{
ll p=0; ll f=1; char ch = getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll p[41][11][4];
ll g[41][1<<11][31]; // i mask sum
ll f[41][1<<11][31]; // i mask sum
ll s[2][31],cur=1;
ll num[1<<11];
int main()
{
freopen("max.in","r",stdin);
freopen("max.out","w",stdout);
ll n = read(); ll m = read(); ll c = read();
for(ll j=0;j<m;j++) for(ll i=1;i<=n;i++) for(ll k=0;k<=c;k++) p[i][j][k] = read();
for(ll i=1;i<=n;i++)
{
for(ll j=0;jm);j++)
{
cur=1; for(ll k=0;k<=c*m;k++) s[cur][k] = 0,s[cur^1][k] = 0; s[cur][0] = 1;
for(ll k=0;k<m;k++) if(j&bin(k))
{
for(ll now=0;now<=c*m;now++)
for(ll nxc=0;nxc<=c;nxc++) s[cur^1][now+nxc] = (s[cur^1][now+nxc] + s[cur][now] * p[i][k][nxc] % Mod ) % Mod;
for(ll now=0;now<=c*m;now++) s[cur][now] = 0; cur ^= 1;
}
for(ll k=0;k<=c*m;k++) g[i][j][k] = s[cur][k];
}
}
for(ll i=0;im);i++) num[i] = __builtin_popcount(i);
f[0][0][0] = 1;
for(ll i=1;i<=n;i++)
{
for(ll j=0;jm);j++) for(ll psta = j; ;psta = (psta-1) & j)
{
ll x = num[j]; ll y = num[psta];
for(ll k=0;k<=c*x;k++) for(ll lst=0;lst<=c*y;lst++)
f[i][j][max(k,lst)] =(f[i][j][max(k,lst)] + (f[i-1][psta ^ j][k] * g[i][psta][lst] % Mod) ) % Mod;
if(psta == 0) break ;
}
}
ll ans = 0 ; for(ll k=0;k<=c*m;k++) ans = (ans + k * f[n][bin(m)-1][k]) % Mod;
return printf("%lld\n",ans),0;
}
小C很喜欢二维染色问题,这天他拿来了一个w h的二维平面,初始时均为
白色.然后他在上面设置了n个关键点(Xi,Yi),对于每个关键点他会选择进行下
列操作的一个:
将x > Xi的部分染成黑色.
将x < Xi的部分染成黑色.
将y > Yi的部分染成黑色.
将y < Yi的部分染成黑色.
图示参见样例解释
他本来是想让你支持单点修改以及可持久化然后把空间限制开成M的,但鉴
于这只是第二题,现在他只想最大化所有操作结束之后白色部分的周长(不难发
现白色部分一定是个矩形).特别地,如果没有白色部分,设其周长为0 (N<=2*10^5)
这道题很好啊
首先一个结论,答案的下限是2*max(w,h)+2 , 肯定经过w/2或者h/2的中轴线,这个可以用反证法或者随便就可以证出来,这样有什么用呢,我们就要利用一下性质
对于中轴线,我们可以维护那些点到中轴线的距离,我们现在假设有上端点和下端点分别是 yr y r , yl y l 那么我们的左端点和右端点要满足
然后这个东西我们就相当于对中轴线的两边维护一个单调栈,表示可以取的下边界,显然我们要维护的是,越下面的下边界越短,然后对纵坐标离散化之后用线段树维护一个值(下边界的长 - 当前纵坐标)因为你的答案要减去纵坐标
对于每个点,你先找到以这个点为上边界的,然后再把它分别加入两边单调栈,具体可以看代码
然后反过来再做一遍就好了
#include
#define ll long long
#define pii pair
using namespace std;
const ll N = 300010;
const ll inf = 2e9;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node
{
ll x,y;
node(){}
node(ll _x,ll _y){x=_x; y=_y;}
}q[N],a[N],b[N]; ll sta,stb;
ll w,h,n;
ll rt,lc[N<<2],rc[N<<2],c[N<<2],lazy[N<<2],tot = 0;
void link(ll &u,ll L,ll R,ll k,ll cc)
{
if(!u) u=++tot,lazy[u] = 0;
if(L==R){c[u] = cc ; return ;}
ll mid = (L+R)>>1;
if(k<=mid) link(lc[u],L,mid,k,cc);
else link(rc[u],mid+1,R,k,cc);
c[u] = max(c[lc[u]] , c[rc[u]]);
}
ll ans = 0;
void push_down(ll u)
{
if(lazy[u])
{
c[lc[u]]+=lazy[u];
c[rc[u]]+=lazy[u];
lazy[lc[u]] += lazy[u]; lazy[rc[u]] += lazy[u];
lazy[u] = 0;
}
}
void chg(ll u,ll L,ll R,ll l,ll r,ll cc)
{
if(l>r) return ;
if(L==l && R==r)
{
c[u] += cc; lazy[u]+=cc;
return ;
}
ll mid=(L+R)>>1;
push_down(u);
if(r<=mid) chg(lc[u],L,mid,l,r,cc);
else if(l>mid) chg(rc[u],mid+1,R,l,r,cc);
else
{
chg(lc[u],L,mid,l,mid,cc);
chg(rc[u],mid+1,R,mid+1,r,cc);
}
c[u] = max(c[lc[u]] , c[rc[u]]);
}
bool cmpy(const node &x,const node &y){return x.y < y.y;}
void solve()
{
sort(q+1,q+n+1,cmpy);
rt=tot=0; memset(c,0,sizeof(c)); memset(lazy,0,sizeof(lazy));
memset(lc,0,sizeof(lc)); memset(rc,0,sizeof(rc));
for(ll i=1;i<=n;i++) link(rt,1,n,i,-inf);
sta = stb = 0; ll mid = w/2;
for(ll i=1;iif(q[i].x<=mid)
{
ll nx = i-1;
while(sta && a[sta].x <= q[i].x)
{
chg(rt,1,n,a[sta].y,nx,a[sta].x - q[i].x);
nx = a[sta].y-1; sta--;
}
a[++sta] = node(q[i].x , nx+1);
}
else
{
ll nx = i-1;
while(stb && b[stb].x >= q[i].x)
{
chg(rt,1,n,b[stb].y,nx,q[i].x - b[stb].x);
nx = b[stb].y-1; stb--;
}
b[++stb] = node(q[i].x , nx+1);
}
a[++sta] = node(0,i);
b[++stb] = node(w,i);
chg(rt,1,n,i,i,inf - q[i].y + w);
ans = max(ans , c[rt] + q[i+1].y);
}
}
int main()
{
w = read(); h = read(); n = read();
for(ll i=1;i<=n;i++) q[i].x = read() , q[i].y = read();
q[++n] = node(0,0); q[++n] = node(w,h);
solve();
swap(w,h); for(ll i=1;i<=n;i++) swap(q[i].x,q[i].y);
solve();
return printf("%lld\n",ans<<1),0;
}
给你一个数列N,询问Q,要你支持区间询问最大值,区间异或,区间与
N<=200000
这道题直接搞就好了?
理由如下:
首先很快想到拆开每一个二进制位来处理
对于二进制相同的位,我们可以打一个标记,因为无论是变成0和变成1 ,都可以一起变
这样的话,一开始的标记最多有20N个
这样考虑你一次修改,如果一个大区间打了标记,而你要进入一个小区间里面,增加的标记的数量是log个的。
但是如果你要修改的是一个大区间,而这个大区间没有被打标记,那么肯定要搜到小区间里面分别修改,而每次搜到一个小区间的结点,减少标记是1
所以这样就可以证明每次你搜到的结点个数是 O(20NlogN) O ( 20 N l o g N )
维护一个区间或,区间与,或和与标记,然后下传的时候,先下传与标记,再下传或标记,避免或标记在后的受到与标记的影响
#include
using namespace std;
const int N = 200010;
const int mx = (1<<20)-1;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
int a[N]; int rt,tot,lc[N<<2],rc[N<<2],land[N<<2],lor[N<<2],sand[N<<2],sor[N<<2],c[N<<2];
void update(int u)
{
sand[u] = sand[lc[u]] & sand[rc[u]];
sor[u] = sor[lc[u]] | sor[rc[u]];
c[u] = max(c[lc[u]] , c[rc[u]]);
}
void push_down(int u)
{
if(land[u]!=mx)
{
land[lc[u]] &= land[u]; land[rc[u]] &= land[u];
lor[lc[u]] &= land[u]; lor[rc[u]] &= land[u];
sand[lc[u]] &= land[u]; sand[rc[u]] &= land[u];
sor[lc[u]] &= land[u]; sor[rc[u]] &= land[u];
c[lc[u]] &= land[u]; c[rc[u]] &= land[u];
land[u] = mx;
}
if(lor[u])
{
lor[lc[u]] |= lor[u]; lor[rc[u]] |= lor[u];
sand[lc[u]] |= lor[u]; sand[rc[u]] |= lor[u];
sor[lc[u]] |= lor[u]; sor[rc[u]] |= lor[u];
c[lc[u]] |= lor[u]; c[rc[u]] |= lor[u];
lor[u] = 0;
}
}
int build(int L,int R)
{
tot++; int now = tot; land[now] = mx,lor[now] = 0;
if(Lint mid=(L+R)>>1;
lc[now] = build(L,mid); rc[now] = build(mid+1,R);
update(now);
}
else c[now] = sand[now] = sor[now] = a[L];
return now;
}
int qry(int u,int L,int R,int l,int r)
{
if(L == l && R == r) return c[u];
int mid=(L+R)>>1;
push_down(u);
if(r<=mid) return qry(lc[u],L,mid,l,r);
else if(l>mid) return qry(rc[u],mid+1,R,l,r);
else return max(qry(lc[u],L,mid,l,mid) , qry(rc[u],mid+1,R,mid+1,r));
}
void cor(int u,int L,int R,int l,int r,int x)
{
if(L == l && R == r)
{
int p = (sand[u] & x) | ((sor[u] ^ mx) & x);
if((p&x) == x)
{
sor[u] |= x; sand[u] |= x;
lor[u] |= x; c[u] |= x;
//printf("SB.. %d %d\n",L,R);
return ;
}
}
int mid=(L+R)>>1;
push_down(u);
if(r<=mid) cor(lc[u],L,mid,l,r,x);
else if(l>mid) cor(rc[u],mid+1,R,l,r,x);
else
{
cor(lc[u],L,mid,l,mid,x);
cor(rc[u],mid+1,R,mid+1,r,x);
}update(u);
}
void cand(int u,int L,int R,int l,int r,int x)
{
if(L == l && R == r)
{
int p = ((sor[u] ^ mx) & (x ^ mx)) | ((x^mx) & sand[u]); //鐩稿悓鏄?
if((p | x) == mx)
{
sor[u] &= x; sand[u] &= x;
land[u] &= x; lor[u] &= x; c[u] &= x;
//printf("SB... %d %d\n",L,R);
return ;
}
}
int mid=(L+R)>>1;
push_down(u);
if(r<=mid) cand(lc[u],L,mid,l,r,x);
else if(l>mid) cand(rc[u],mid+1,R,l,r,x);
else
{
cand(lc[u],L,mid,l,mid,x);
cand(rc[u],mid+1,R,mid+1,r,x);
}update(u);
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int n = read(); int q = read();
for(int i=1;i<=n;i++) a[i] = read();
rt=tot=0; rt=build(1,n);
while(q--)
{
int op = read();
if(op==3)
{
int l=read(); int r=read();
printf("%d\n",qry(rt,1,n,l,r));
}
else
{
int l = read(); int r = read(); int x = read();
if(op==1) cand(rt,1,n,l,r,x);
else cor(rt,1,n,l,r,x);
}
}
return 0;
}
A和B分别操作一颗树上的结点,定义KA为A的连通块的个数,KB为B的连通块的个数,A想让A-B的连通块尽量的多,B想让A-B的连通块尽量的少
首先连通块的数量 = 点数 - 边数
考虑只有两个点都是同一个颜色的,连通块个数才会-1
因为答案是
那么对于边的两个端点分别+1,然后A取的话就-去这个点的权值,B取的话+这个点的权值
那么一条边两个端点是不同的人取的话,那么贡献就抵消了,否则A取的话-2,B取的话+2,B想让上式最小,A想让上式最大,都是取权值尽量小的
现在我们是求出了
代码没存不见了233333。。。。
这个20行的事嘛
给每个点一个power值,对其他的点的影响为 max(0,poweri−dis(posi−j)) m a x ( 0 , p o w e r i − d i s ( p o s i − j ) ) 然后求出每个点的贡献
N<=200000;M<=500000
这道题用点分治做,我们不能一个个点对去求
有一个想法就是对于一个子树x和一个子树y,考虑x的子树影响到y
肯定有 powerx−dx≥dy p o w e r x − d x ≥ d y
然后可以用树状数组来维护 powerx−dx p o w e r x − d x ,对于 dy d y 只需要找后缀和就好了
对于power比较大的点就直接统计
这样做法的时间复杂度是 O(Nlog2N) O ( N l o g 2 N )
我们要更优的做法,只需要用一个桶之类的维护 powerx−dx p o w e r x − d x ,然后再bfs找y的子树就好了
深度是递增的,然后维护一个 powerx−dx p o w e r x − d x 的和然后减掉即可
注意还需要计算重心连通块内的贡献
#include
#define pb push_back
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair pii;
const ll N = 200010;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
vector G[N]; ll n,m; vector h[N]; ll ans[N];
bool bo[N]; ll p=0,mx,siz[N];
void dfs1(ll x,ll f)
{
siz[x] = 1;
for(ll i=0;iif(y==f || bo[y]) continue;
dfs1(y,x); siz[x] += siz[y];
}
}
void find_root(ll x,ll f,ll tot)
{
ll s = tot - siz[x];
for(ll i=0;iif(bo[y] || y==f) continue;
find_root(y,x,tot); s = max( s , siz[y]);
}
if(s < mx){mx = s; p = x;}
}
queue >q; ll cnt[N] , sum[N]; ll sp,gs = 0; // 桶内和桶外的和,桶内和桶外的个数
bool vis[N];
void bfs(ll ss,ll dep)
{
while(!q.empty()) q.pop();
vis[ss] = 1; q.push(MP(ss,dep)); ll now = 0; ll s = sp; ll c = gs;
while(!q.empty())
{
pii x = q.front();
while(now < x.second){c-=cnt[now]; s-=sum[now]; now ++;}
ans[x.first] += s - c * x.second;
for(ll i=0;iif(bo[y] || vis[y]) continue;
vis[y] = 1; q.push(MP(y,x.second+1));
}q.pop();
}
}
void calc(ll x,ll f,ll dep,ll op)
{
vis[x] = 0;
for(ll i=0;iif(y<=0) continue; sp += y * op; gs += op;
if(y <= n) sum[y] += y * op,cnt[y]+=op;
}
for(ll i=0;iif(y==f || bo[y]) continue;
calc(y,x,dep+1,op);
}
}
void dfs(ll x,ll f)
{
dfs1(x,0); mx = INT_MAX; find_root(x,0,siz[x]); x=p;
gs = sp = 0; vis[x] = 1;
for(ll i=0;iif(bo[y] || y==f) continue;
bfs(y,1);
calc(y,x,1,1);
}
vis[x] = 0;
for(ll i=0;iif(bo[y] || y==f) continue;
calc(y,x,1,-1);
}
for(ll i=0;i1;
if(y<=n) sum[y] += y,cnt[y] ++;
}
vis[x] = 1;
for(ll i=G[x].size()-1;i>=0;i--)
{
ll y = G[x][i];
if(bo[y] || y==f) continue;
bfs(y,1);
calc(y,x,1,1);
}ans[x] += sp;
vis[x] = 0;
for(ll i=G[x].size()-1;i>=0;i--)
{
ll y = G[x][i];
if(bo[y] || y==f) continue;
calc(y,x,1,-1);
}
for(ll i=0;i1;
if(y<=n) sum[y] -= y,cnt[y] --;
}
bo[x] = 1;
for(ll i=0;iif(bo[y] || y==f) continue;
dfs(y,x);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n = read(); m = read(); for(ll i=1;i1); G[i+1].pb(x);}
while(m--)
{
ll x = read(); ll y = read();
h[x].pb(y);
}
dfs(1,0);
for(ll i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}
求有多少N 个的竞赛图包含至少一个长度为K 的简单环, 输出答案模
10^9 + 7 的结果.
竞赛图: 任意两个点之间都有一条有向边的图.
简单环: 不经过重复节点的回路.
首先对于一个竞赛图,我们缩完联通分量之后,剩下的肯定是一个DAG,而且是一条链的形式,然后我们就可以用 dp[i][0/1] d p [ i ] [ 0 / 1 ] 来解决这个问题,除此之外,我们还要预处理出大小为k的强联通的方案数,考虑到这是一个竞赛图,就有
g[n]=2(n2)+∑i=1n−1(ni)g[i]2(n−i2) g [ n ] = 2 ( n 2 ) + ∑ i = 1 n − 1 ( n i ) g [ i ] 2 ( n − i 2 )
#include
using namespace std;
typedef long long ll;
const ll Mod = 1e9+7;
const ll N = 5010;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll g[N]; ll bin[N]; ll f[N][2]; int C[N][N];
void upd(ll &x,ll y){x=(x+y) % Mod;}
ll qpow(ll x,ll k,ll mo)
{
ll s=1; while(k){if(k&1) s=s*x%mo; x=x*x%mo; k>>=1;} return s;
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
ll n = read(); ll k = read();
C[0][0] = 1;for(ll i=1;i<=n;i++)
{
C[i][0] = 1;
for(ll j=1;j<=i;j++) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % Mod;
}
for(ll i=1;i<=n;i++) bin[i] = qpow(2,i*(i-1)/2,Mod);
for(ll i=1;i<=n;i++)
{
g[i] = bin[i];
for(ll j=1;jmemset(f,0,sizeof(f)); f[0][0] = 1;
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=i;j++)
{
if(j>=k)
upd(f[i][1] , ((f[i-j][0] + f[i-j][1]) % Mod * g[j] % Mod * C[n-(i-j)][j] % Mod) );
else
upd(f[i][1] , (f[i-j][1] * g[j] % Mod * C[n-(i-j)][j] % Mod)),
upd(f[i][0] , (f[i-j][0] * g[j] % Mod * C[n-(i-j)][j] % Mod));
}
}
return printf("%lld\n",f[n][1]),0;
}