problem D(10) Popcorn n个物品取m个方法数C(n,m)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+20;
int main()
{
//freopen("popcorn.in","r",stdin);
int t;
cin>>t;
while(t--)
{
int n,m,ans=1;
cin>>n>>m;
//C(n,m)=n!/(n-m)!m!
//=n-m+1..n / 1..m
for(int i=1;i<=m;i++)
{
ans*=n-i+1;
ans/=i;
}
cout<<ans<<endl;
}
return 0;
}
Problem H(20) Commandos(简单DP)
10*10*10三维地图,点A(F,Y,X)每次可以移动到(F-1,Y,X),(F,Y+1,X),(F,Y,X+1);有N<=1e3个地点(F,Y,X)有人质a[i] 求从点(10,1,1)出发最多能救多少人质
从移动方式得出该图是为无环的 所以dp[f][x][y]表示从(f,x,y)出发最多能救多少人 记忆化转移状态即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=20;
int n;
int g[N][N][N],dp[N][N][N];
int DP(int f,int x,int y)
{
if(dp[f][x][y]!=-1)
return dp[f][x][y];
int &res=dp[f][x][y];
if(f<1||x>10||y>10)
return 0;
res=max(DP(f-1,x,y),max(DP(f,x+1,y),DP(f,x,y+1)))+g[f][x][y];
return res;
}
int main()
{
//freopen("commandos.in","r",stdin);
int t;
cin>>t;
while(t--)
{
int f,x,y,h;
cin>>n;
memset(dp,-1,sizeof(dp));
memset(g,0,sizeof(g));
for(int i=0;i<n;i++)
{
cin>>f>>x>>y>>h;
g[f][x][y]=h;
}
cout<<DP(10,1,1)<<endl;
}
return 0;
}
Problem E(30) Jumping (建图后BFS)
题意:x轴上有点1~N,每个点i可以跳到i-a[i],i+a[i].求每个点i跳到n的最少次数
不是无环图 无法DP. 建图:若i能跳到j 则j->i 连接一条边,初始时把定点n压入队列做BFS从小到大更新答案即可 O(2*N)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+20;
const int inf=2e6;
int n,a[N],ans[N],vis[N];
vector<int> e[N];
struct node{
int u,d;
node(int a,int b):u(a),d(b)
{
}
};
queue<node> q;
void bfs()
{
while(!q.empty())
q.pop();
q.push(node(n,0));
ans[n]=0;
vis[n]=1;
while(!q.empty())
{
node t=q.front();
q.pop();
int u=t.u,d=t.d;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(!vis[v])
{
vis[v]=1;
ans[v]=d+1;
q.push(node(v,d+1));
}
}
}
}
int main()
{
// freopen("jumping.in","r",stdin);
int t;
cin>>t;
while(t--)
{
cin>>n;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
ans[i]=-1;
scanf("%d",&a[i]);
//i能到j j->i
if(i-a[i]>=1)
e[i-a[i]].push_back(i);
if(i+a[i]<=n)
e[i+a[i]].push_back(i);
}
bfs();
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]),e[i].clear();
}
return 0;
}
Problem J(40) Whistle's to the car (dfs+树上差分)
题意:n个结点,带权的树,v控制u当且仅当 u为v的子孙&& dist[u,v]<=a[u],n<=2e5,问每个结点控制的数量?
设d[i]为i->rt的距离,dist[u,v]=d[u]-d[v],根据u来更新答案,控制u的v必须满足 ,d[v]>=d[u]-a[u]
d[i]随着深度递增,若在dfs过程中保存u的祖先的d值,可以二分找到第一个满足的pos,则ans[pos~fa[u]]都应该+1
怎么判断祖先v被加了多少次?
sum[pos~fa[u]]+1,在树上进行差分:让sum[fa[pos]]--,sum[fa[u]]++,问v被加了多少次? 只要累加v的子树的sum,(v不在某个u的[pos~fa[u],则累加时,该段sum被抵消)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> ii;
const int N=2e6+20;
ll a[N],sum[N],d[N],n;
vector<ii> e[N];//w,v
vector<ii> que;
void dfs(int u,int fa)
{
sum[fa]++;
ll t=d[u]-a[u];
int pos=lower_bound(que.begin(),que.end(),ii(t,0))-que.begin();
pos--;//fa[pos]
if(pos>=0)
sum[que[pos].second]--;
que.push_back(ii(d[u],u));
for(int i=0;i<e[u].size();i++)
{
ll v=e[u][i].second,w=e[u][i].first;
if(v==fa) continue;
d[v]=d[u]+w;
dfs(v,u);
sum[u]+=sum[v];//
}
que.pop_back();//u不在为任何结点的祖先
}
int main()
{
freopen("car.in","r",stdin);
int t;
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]),e[i].clear(),sum[i]=0,d[i]=0;
ll p,w;
int i;
for(int k=2;k<=n;k++)
{
scanf("%d%I64d%I64d",&i,&p,&w);
e[i].push_back(ii(w,p));
e[p].push_back(ii(w,i));
}
dfs(1,0);
for(int i=1;i<=n;i++)
printf("%I64d%c",sum[i],i==n?'\n':' ');
}
return 0;
}
Problem G(40) The Galactic Olympics 排列+第二类Stirling数
题意:k不同个人,n场不同比赛 k<=1e6,n<=1e3,每人至少要参加一场比赛,问方法数?
先转化成:对于n个不同的球放入,k个相同的盒子 每个盒子不为空方法数 即第二类Stirling
考虑最后一个人单独一个盒子 或者放到某个盒子中.则S(n,k)=S(n-1,k-1)+S(n-1,k)*k
最后在考虑k个人的顺序 ans=S(n,k)*k!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+20;
const ll mod=1e9+7;
ll S[N][N];
int main()
{
// freopen("galactic.in","r",stdin);
memset(S,0,sizeof(S));
S[0][0]=S[1][1]=1;
for(ll i=2;i<=2000;i++)
{
for(ll j=1;j<=i;j++)
{
S[i][j]=(S[i-1][j-1]+S[i-1][j]*(ll)j)%mod;
}
}
int t;
cin>>t;
while(t--)
{
ll n,k;
cin>>n>>k;
if(k>n)
{
puts("0");
continue;
}
ll ans=S[n][k];
for(ll i=1;i<=k;i++)
ans=(ans*i)%mod;
cout<<ans<<endl;
}
return 0;
}
Problem A(50) The Game of osho 博弈SG
题意:给出G(G<100)个子游戏,每个子游戏由(Bi,Ni)(Bi,Ni<=1e9),每次可以使Ni-Bi^k(k=0,1,2..) Bi^k<=Ni玩家可以选择任意一个子游戏操作,不能操作算输
子游戏:相当于nim游戏中的一堆石子
因为b^0=1 所以最终局面肯定为(0,0...0),求出f(i):第i个子游戏的SG(mex)值
若f(1)^f(2)^..f(n)=x(x!=0) 则必然有后继状态SG值为f[i]^x 则可以转移到0,所以SG值异或和!=0为必胜态(每次都可以转移到0)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+20;
/*
ll SG(ll b,ll n)
{
set<ll> s;
if(n==0)
return 0;
if(b==1)
return n;
ll x=1;
while(x<=n)
{
s.insert(SG(b,n-x));
x=x*b;
}
ll y=0;
while(s.count(y))//logn(b)
y++;
return y;
}
*/
//打表得规律
ll calc_SG(ll b,ll n)
{
if(b%2)
{
if(n%2)
return (ll)1;
else
return (ll)0;
}
else
{
if((n-b)%(b+1)==0)
return (ll)2;
ll p=n/(b+1);//反转多少次 (1,0)->(0,1)->(1,0) ...
if(p%2)
return n%2==0;
else
return n%2;
}
}
int main()
{
//freopen("powers.in","r",stdin);
int t;
cin>>t;
while(t--)
{
ll G,b,n;
cin>>G;
ll ans=0;
for(int i=0;i<G;i++)
{
cin>>b>>n;
ans^=calc_SG(b,n);
}
if(ans)
puts("1");
else
puts("2");
}
return 0;
}