A
dp
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 110;
int n,p;
int a[maxn];
ll f[maxn][2];
int main()
{
scanf("%d%d",&n,&p);
f[0][0]=1ll;
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x); x&=1;
f[i][0]=f[i-1][0],f[i][1]=f[i-1][1];
if(x) f[i][1]+=f[i-1][0],f[i][0]+=f[i-1][1];
else f[i][1]+=f[i-1][1],f[i][0]+=f[i-1][0];
}
printf("%lld\n",f[n][p]);
return 0;
}
B
转化模型,n-1个数,每个数绝对值在C~D之间,问存不存在一种方案使他们的和为A-B
考虑去掉绝对值,枚举负数个数i,那么i个负数的和的范围,n-1-i个正数和的范围就都知道了,两个不等式相加,看A-B在不在范围内
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 1100;
ll n,a,b,c,d,u;
int main()
{
scanf("%lld%lld%lld%lld%lld",&n,&a,&b,&c,&d);
u=b-a; n--;
ll l=0,r=0,L=c*n,R=d*n;
for(int i=n;i>=0;i--)
{
if(L+l<=u&&u<=R+r) { puts("YES");return 0; }
L-=c,R-=d;
l-=d,r-=c;
}
puts("NO");
return 0;
}
C
转化为线段覆盖的模型就很妙了
若权为x的球有k个,它能覆盖x-k~x之间的线段,易证0~n全部被覆盖时不需要修改,若没有全部覆盖,易证未被覆盖的线段数就是至少要修改的次数
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
int n,m;
int v[maxn];
int p[maxn],a[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&p[i]),a[p[i]]++;
for(int i=1;i<=n;i++)
for(int j=max(i-a[i],0);j<i;j++) v[j]++;
int ans=0;
for(int i=0;i<n;i++) if(!v[i]) ans++;
while(m--)
{
int x,y; scanf("%d%d",&x,&y);
int c=p[x];
if(c-a[c]>=0&&v[c-a[c]]==1) ans++;
if(c-a[c]>=0) v[c-a[c]]--; a[c]--;
c=y; p[x]=c;
if(c-a[c]-1>=0&&!v[c-a[c]-1]) ans--;
a[c]++; if(c-a[c]>=0) v[c-a[c]]++;
printf("%d\n",ans);
}
return 0;
}
D
经典博弈,将一条长度为x的链视为一堆石子数为x的一堆石子,每个点的所有孩子分别各自合并到一条链,这个点相当于在这些堆石子中决策,将sg异或起来,合并成一条链
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 110000;
int n,m;
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int sg[maxn];
void dp(const int x,const int fa)
{
sg[x]=0;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa)
{
dp(y,x); sg[x]^=(sg[y]+1);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y; scanf("%d%d",&x,&y);
ins(x,y); ins(y,x);
}
dp(1,0);
if(sg[1]) puts("Alice");
else puts("Bob");
return 0;
}
E
尝试去构造一个积木间的匹配关系
我们用二元组
(l,r)
代表每个积木,
若一个积木
C=0,l=+A,C≠0,l=−A,D=0,r=−B,D≠0,r=+D
如果在
(l,r)
后面能拼上
(l′,r′)
,需要满足r=l’,否则就是不拼的情况,要求r<0,l’>0。
建一个图,点标号-H~+H,对每个二元组
(l,r)
,l向r连一条有向边
题目可以转化成判断是否可以将图中的边分成若干条链要求每条链从正权点出发,负权点结束。
对于正权点,要求outdegree>=indegree,
负权点要求indegree>=outdegree,
且对于同种每个弱联通的联通块,其中必定要有出入度不等的点
满足这些条件后,对于每个弱联通块,如果它没有环,易知一定存在解;若有环,环一定和链相接,可以通过构造,把环拼在链上。
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 410000;
int n,m=800;
int fa[maxn];
int findfa(const int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
int d[maxn],v[maxn],ok[maxn];
int main()
{
scanf("%d%*d",&n);
for(int i=1;i<m;i++) fa[i]=i;
for(int i=1;i<=n;i++)
{
int a,b,c,D,l,r; scanf("%d%d%d%d",&a,&b,&c,&D);
l=!c?a:-c;
r=!D?-b:D;
l+=400,r+=400;
d[l]++,d[r]--; v[l]=v[r]=1;
fa[findfa(l)]=findfa(r);
}
for(int i=1;i<=400;i++) if(d[i]>0) return puts("NO"),0;
for(int i=400;i<m;i++) if(d[i]<0) return puts("NO"),0;
for(int i=1;i<m;i++) if(d[i]) ok[findfa(i)]=1;
for(int i=1;i<m;i++) v[findfa(i)]|=v[i];
for(int i=1;i<m;i++) if(i==findfa(i)&&v[i]&&!ok[i])
return puts("NO"),0;
puts("YES");
return 0;
}
F
考虑一种朴素的dp,f[i][k]表示处理到第i条链,第i条链在1~n-1层走的方向为k
直接枚举下一条链的状态l转移dp,是
O(m4n)
的,考虑合并类似的合法状态
令s[i]表示一个状态前i位1的数量,下一条链的状态l如果是合法的,当且仅当对于任意的u,有sk[u]<=sl[u]
所以我们可以利用这个合并一些s[u]相同的状态
(应该有很多种状态定义都行)以下是题解的状态定义:
令f[i][j][k]表示处理到第i条链的第j次操作且前j次操作与k相同,转移的时候枚举第j+1次操作,
如果往左走:要求k的第j+1位为0且这一位没有往右的限制
如果往右:要求没有往左的限制,如果k的j+1位为1就可以直接转移至f[i][j+1][k],否则把k往后第一个1移到j+1位,转移至f[i][j+1][newk]
注意空间,把i和j都滚动
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
#define _ %Mod
#define __ %=Mod
using namespace std;
const int maxn = 22;
const int maxm = 22;
const int maxk = 1100000;
const ll Mod = 1e9+7;
int n,m,K;
int ci[maxm][maxn];
ll f[2][maxk];
int main()
{
scanf("%d%d%d",&n,&m,&K); n--;
memset(ci,-1,sizeof ci);
for(int i=1;i<=K;i++)
{
int x,y,z; scanf("%d%d%d",&x,&y,&z);
ci[x][y-1]=z;
}
int al=1<<n;
int now=0; f[0][0]=1ll;
for(int i=1;i<=m;i++) for(int j=0;j<n;j++)
{
for(int k=0;k<al;k++) if(f[now][k])
{
ll tmp=f[now][k];
if(!(k>>j&1)&&ci[i][j]!=1) (f[!now][k]+=tmp)__;
if(ci[i][j]==0) continue;
if(k>>j&1) (f[!now][k]+=tmp)__;
else
{
int lk=k&(1<<j+1)-1,nk=k^lk;
nk=nk^lowbit(nk)^lk^1<<j;
(f[!now][nk]+=tmp)__;
}
}
memset(f[now],0,sizeof f[now]); now=!now;
}
ll ans=0;
for(int k=0;k<al;k++) (ans+=f[now][k])__;
printf("%lld\n",ans);
return 0;
}