A
发现组合出的数一定比最大的ai小,且一定是gcd的倍数(可以构造出gcd)
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 gcd(int a,int b){return a==0?b:gcd(b%a,a);}
int n,k;
int a[maxn];
int main()
{
scanf("%d%d",&n,&k);
int d=0,mx=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
d=gcd(d,a[i]); if(a[i]>mx) mx=a[i];
if(a[i]==k) { puts("POSSIBLE");return 0; }
}
if(mx>k&&k%d==0) puts("POSSIBLE");
else puts("IMPOSSIBLE");
return 0;
}
B
对于选出一些活动的状态,瓶颈是最大人数的活动x,而要减少最大人数,要么选出新的活动使得x的参加人数减少,要么去掉x
我们先选出所有活动,此时最大人数的活动x,不能再添加活动使其减少,若要答案更优,一定要去掉x,所以去寻找不含x的情况下的最优解,递归下去,复杂度nm^2
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 = 310;
int n,m;
int a[maxn][maxn];
bool v[maxn];
int ans;
int d[maxn];
void solve(const int num)
{
if(num==1) return;
for(int i=1;i<=m;i++) d[i]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) if(v[a[i][j]])
{
d[a[i][j]]++;
break;
}
}
int now=0;
for(int i=1;i<=m;i++) if(d[i]>d[now]) now=i;
if(ans>d[now]) ans=d[now];
v[now]=false;
solve(num-1);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
}
for(int i=1;i<=m;i++) v[i]=true;
ans=n;
solve(m);
printf("%d\n",ans);
return 0;
}
C
因为每个人一定被某种类型选到,我们知道了X+Y个数是哪些,就知道剩下Z个数是哪些,所以我们可以转化模型,将(A,B,C)变成(A-C,B-C,C),变成在X+Y+Z个数中选X个数取它的A,Y个数取它的B,和的最大值再加上C。
假设我们选出了X+Y个数,我们将他们和没被选出的数一起按照a-b排序,发现对于两组数i(a,b,c),j(A,B,C),(i< j)有a-b<=A-B -> a+B<=A+b,即我们选的X个数一定在Y个数右边(如果有在左边的,交换位置不会变劣)
可以用堆搞出prei,sufi,枚举断点找最大值
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 inf 1e12
using namespace std;
const int maxn = 110000;
int X,Y,Z,n;
ll a[maxn],b[maxn],c[maxn];
bool v[maxn];
struct node{ll a,b,c;}s[maxn];
inline bool cmpc(const node x,const node y){return x.c<y.c;}
ll ans;
ll pre[maxn],suf[maxn];
struct E{ll x;};
inline bool operator <(const E x,const E y){return x.x>y.x;}
priority_queue<E>q;
int main()
{
scanf("%d%d%d",&X,&Y,&Z); n=X+Y+Z;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]); ans+=c[i];
s[i]=(node){a[i]-c[i],b[i]-c[i],a[i]-b[i]};
}
sort(s+1,s+n+1,cmpc);
for(int i=1;i<=n;i++)
{
pre[i]=pre[i-1];
if(i>Y)
{
if((q.top()).x<s[i].b)
pre[i]-=(q.top()).x,q.pop(),pre[i]+=s[i].b,q.push((E){s[i].b});
}
else pre[i]+=s[i].b,q.push((E){s[i].b});
}pre[0]=-1e12;
while(!q.empty()) q.pop();
for(int i=n;i>=1;i--)
{
suf[i]=suf[i+1];
if(n-i>=X)
{
if((q.top()).x<s[i].a)
suf[i]-=(q.top()).x,q.pop(),suf[i]+=s[i].a,q.push((E){s[i].a});
}
else suf[i]+=s[i].a,q.push((E){s[i].a});
}suf[n+1]=-1e12;
ll now=LLONG_MIN;
for(int i=1;i<=n;i++) if(i>=Y&&n-i>=X)
{
ll temp=pre[i]+suf[i+1];
if(temp>now) now=temp;
}
ans+=now;
printf("%lld\n",ans);
return 0;
}
D
考虑每条边对答案的贡献,假设它连接的两个联通块较小的size是s,如果是哈密顿回路,它的贡献最大为
2cs
。
在树有双重心的情况下,我们一定能构造一个回路使得每条边的贡献达到最大(在左右两个重心的子树内交替跳),这时我们要考虑对于哈密顿回路上的相邻点u,v,删掉u,v路径上的边权和,我们发现对于双重心x,y之间的边(x,y),当且仅当上文的构造回路方法能使得它被经过n次,其他情况最多只有n-1次,此时回路上的任意相邻点都不在一棵重心的子树上,怎么删都一定会删掉这条边,所以这条边被经过的上界是n-1,而我们有一种方法构造使得回路变路径只需要删掉这条边(x为路径的出发点,y为终点,2棵子树交替跳),所以
∑2cs−(x,y)
就是最优解
对于树重心
G
唯一的情况,类似的,和重心相连的边中一定有一条经过次数不能达到
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 inf 1e9
using namespace std;
inline void down(int &x,const int &y){if(x>y)x=y;}
const int maxn = 210000;
int n,m;
struct edge{int y,c,nex;}a[maxn]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=(edge){y,c,fir[x]};fir[x]=len;}
int siz[maxn],rt,uc[maxn];
void dfs(const int x,const int fa)
{
bool flag=true; siz[x]=1;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa)
{
uc[y]=a[k].c; dfs(y,x); siz[x]+=siz[y];
if(siz[y]*2>n) flag=false;
}
if((n-siz[x])*2>n) flag=false;
if(flag) rt=x;
}
ll ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y,c; scanf("%d%d%d",&x,&y,&c);
ins(x,y,c); ins(y,x,c);
}
dfs(1,0);
for(int i=1;i<=n;i++) ans+=uc[i]*2ll*min(siz[i],n-siz[i]);
int rt2=-1;
for(int k=fir[rt],y=a[k].y;k;k=a[k].nex,y=a[k].y)
{
if(siz[y]>siz[rt])
{
if(siz[rt]*2<=n) rt2=y;
}
else if((n-siz[y])*2<=n) rt2=y;
}
if(rt2==-1)
{
int mc=inf;
for(int k=fir[rt],y=a[k].y;k;k=a[k].nex,y=a[k].y) down(mc,a[k].c);
ans-=(ll)mc;
}
else
{
for(int k=fir[rt],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y==rt2)
ans-=(ll)a[k].c;
}
printf("%lld\n",ans);
return 0;
}
E
我们定义f(x,y)表(0,0)到(x,y)的方案数,有
∑xi=0f(i,y)=f(x+1,y)
(枚举最后一条竖边的位置)
从而
∑xi=0∑yj=0f(i,j)=f(x+1,y+1)−1
(横着用一次,竖着用一次第一条柿子)
于是有
∑dxi=sx∑dyj=syf(i,j)=f(dx+1,dy+1)−f(sx,dy+1)−f(dx+1,sy)+f(sx,sy)
可以发现一个点到一个矩阵内任意点的方案数只和其到4个点的带权方案数相关,反之亦然。
我们先忽略矩阵B,矩阵A到矩阵C的方案数=矩阵A到4个带权点方案数=4个点到4个点的带权方案数。
接着考虑中间矩阵B(这部分看代码更好懂?),一条路径要乘上它和矩阵B的交点个数,而交点个数其实只和进出点相关,贡献是
路径∗k=路径∗(dx+dy−sx−sy+1)
,然后我们把
dxdy
和
sxsy
拆开来算贡献,枚举矩阵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
#define inf 1e9
using namespace std;
const int maxn = 2100000;
const ll Mod = 1e9+7;
ll pw(ll x,int k)
{
ll re=1ll;
for(;k;k>>=1,x=x*x%Mod) if(k&1)
re=re*x%Mod;
return re;
}
ll s[maxn],pinv[maxn];
void pre()
{
s[0]=1ll;
for(ll i=1;i<maxn;i++) s[i]=s[i-1]*i%Mod;
pinv[maxn-1]=pw(s[maxn-1],Mod-2);
for(ll i=maxn-2;i>=0;i--) pinv[i]=pinv[i+1]*(i+1ll)%Mod;
}
ll C(const int i,const int j){return s[i]*pinv[j]%Mod*pinv[i-j]%Mod;}
int x[6],y[6];
struct node{int x,y;ll v;}a[4],b[4];
ll cal(const node x,const node y){return C(y.x+y.y-x.x-x.y,y.x-x.x);}
ll ans;
int main()
{
pre();
for(int i=0;i<6;i++) scanf("%d",&x[i]);
for(int i=0;i<6;i++) scanf("%d",&y[i]);
a[0]=(node){x[0]-1,y[0]-1,1ll};
a[1]=(node){x[0]-1,y[1],-1ll};
a[2]=(node){x[1],y[0]-1,-1ll};
a[3]=(node){x[1],y[1],1ll};
b[0]=(node){x[4],y[4],1ll};
b[1]=(node){x[4],y[5]+1,-1ll};
b[2]=(node){x[5]+1,y[4],-1ll};
b[3]=(node){x[5]+1,y[5]+1,1ll};
ans=0;
for(int i=0;i<4;i++) for(int j=0;j<4;j++)
{
ll now=0;
for(int k=y[2];k<=y[3];k++)
(now+=(ll)(-x[2]-k+1)*cal(a[i],(node){x[2]-1,k})%Mod*cal((node){x[2],k},b[j])%Mod)%=Mod;
for(int k=x[2];k<=x[3];k++)
(now+=(ll)(-k-y[2]+1)*cal(a[i],(node){k,y[2]-1})%Mod*cal((node){k,y[2]},b[j])%Mod)%=Mod;
for(int k=y[2];k<=y[3];k++)
(now+=(ll)(k+x[3])*cal(a[i],(node){x[3],k})%Mod*cal((node){x[3]+1,k},b[j])%Mod)%=Mod;
for(int k=x[2];k<=x[3];k++)
(now+=(ll)(k+y[3])*cal(a[i],(node){k,y[3]})%Mod*cal((node){k,y[3]+1},b[j])%Mod)%=Mod;
ans+=now*a[i].v*b[j].v%Mod;
}
printf("%lld\n",(ans%Mod+Mod)%Mod);
return 0;
}
F
每棵子树的和都为1或-1,都是奇数
如果一个点x有奇数个孩子,则它的权一定为偶,有偶数个孩子则它的权一定为奇,若它在2棵树内的奇偶性不同则一定无解,否则有2种方法构造出解:
author’s solution:
新建一个图,每个点在A、B树的点为x,x’,若x的度数为奇(偶数个孩子),x和x’连边,两棵树内的边照连,新建一个点G和A,B树的根连边,此时每个点度数都为偶数,一定存在一个欧拉回路。
对于一个奇数点,如果它在欧拉回路中由x->x’,他的权为1,否则为-1,对于任意子树,进入子树内的次数=离开子树内的次数,其中进入或离开有1次是走树边,易知子树和一定是1或-1
admin’s solution:
两棵树分别考虑,对于每棵子树,里面的2K+1个奇数点匹配成2K对,我们令每对点一个为1,一个为-1,就能使每个子树权都为1或-1。
新建一个图,对于A树中的配对(x,y),连红边,B树中的配对(x,y),连蓝边,对于新图中的环,一定是红蓝边交错的(一个点不可能匹配两个点所以不可能有相连的同颜色边),所以新图不存在奇环,一定是二分图,所以一定能构造出一组解
(我写了第二种的因为好写233)
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;
vector<int>V[maxn];
struct edge{int y,nex;}a[maxn]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int rt,d[maxn],d2[maxn];
int v[maxn],oth[maxn];
int t[maxn],tp;
void dfs(const int x)
{
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) dfs(y);
tp=0;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) t[++tp]=oth[y];
for(int i=2;i<=tp;i+=2)
{
int x=t[i-1],y=t[i];
V[x].push_back(y); V[y].push_back(x);
}
if(d[x]&1) oth[x]=t[tp];
else oth[x]=x;
}
void col(const int x)
{
for(int i=0;i<V[x].size();i++)
{
const int y=V[x][i];
if(v[y]==2) v[y]=-v[x],col(y);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
if(x==-1) rt=i;
else ins(x,i),d[x]++;
}
dfs(rt);
memset(fir,0,sizeof fir); len=0;
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
if(x==-1) rt=i;
else ins(x,i),d2[x]++;
}
for(int i=1;i<=n;i++) if((d[i]&1)!=(d2[i]&1)) { puts("IMPOSSIBLE");return 0; }
for(int i=1;i<=n;i++) if(!(d[i]&1)) v[i]=2;
dfs(rt);
for(int i=1;i<=n;i++) if(v[i]==2) v[i]=1,col(i);
puts("POSSIBLE");
for(int i=1;i<=n;i++) printf("%d ",v[i]);
return 0;
}