省选测试7
总结
凭着第三题一个剪枝到了第二
\(T1\) 基环树基本都能想到,大部分人都是实现出了问题
\(T2\) \(8 \times n^2\) 能够 \(2e4\) 挺玄学的
\(T3\) 加个剪枝 \(n^22^n\) 就过了
A. 同桌的你
分析
因为每一个点只有一个出度所以最终一定会形成一个基环森林
然后每一次找出环,把环断掉跑一个最大匹配就行了
一种写法是只断一条边然后然后强制这条边选或者不选,还有一种写法是分别断一个环上的两条边后选一个最大的
因为要在匹配数尽量大的前提下让异性配对的人尽量多
所以可以把同性之间的边权设为 \(1e6\),异性之间的边权设为 \(1e6+1\)
第一问答案就是最大匹配除以 \(1e6\),第二问的答案就是最大匹配对 \(1e6\) 取模
输出方案的时候找一下最优决策点就行了
代码
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cmath>
#include<set>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int t,n,a[maxn],x[maxn],h[maxn],tot=2,sta[maxn],jl1,jl2,tp;
#define Max(a,b) (a>b?a:b)
struct asd{
int to,nxt,val;
}b[maxn<<1];
void ad(rg int aa,rg int bb,rg int cc){
b[tot].to=bb;
b[tot].nxt=h[aa];
b[tot].val=cc;
h[aa]=tot++;
}
bool vis[maxn],ishuan,cut[maxn<<1];
void dfs(rg int now,rg int lat,rg int pre){
vis[now]=1;
sta[++tp]=now;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
if(vis[u]){
if(!ishuan) jl1=i,jl2=pre,ishuan=1;
} else {
dfs(u,now,i);
}
}
}
long long f[maxn][2],ans,g[maxn][2];
void dfs2(rg int now,rg int lat){
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || cut[i]) continue;
dfs2(u,now);
f[now][0]+=Max(f[u][1],f[u][0]);
}
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || cut[i]) continue;
f[now][1]=Max(f[now][1],f[now][0]-Max(f[u][0],f[u][1])+f[u][0]+b[i].val);
}
}
void pri(rg int now,rg int lat,rg int op){
if(op==0){
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(cut[i] || u==lat) continue;
pri(u,now,f[u][1]>f[u][0]);
}
} else {
rg int jud=0;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || cut[i]) continue;
if(!jud && f[now][1]==f[now][0]-Max(f[u][0],f[u][1])+f[u][0]+b[i].val){
printf("%d %d\n",now,u);
jud=1;
pri(u,now,0);
} else {
pri(u,now,f[u][1]>f[u][0]);
}
}
}
}
int judrt[maxn];
int main(){
t=read();
while(t--){
memset(h,-1,sizeof(h));
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
memset(cut,0,sizeof(cut));
ans=0,tot=2;
n=read();
for(rg int i=1;i<=n;i++){
vis[i]=0;
a[i]=read(),x[i]=read();
}
for(rg int i=1;i<=n;i++){
if(a[a[i]]!=i){
if(x[i]==x[a[i]]){
ad(i,a[i],1e6);
ad(a[i],i,1e6);
} else {
ad(i,a[i],1e6+1);
ad(a[i],i,1e6+1);
}
} else {
if(i<a[i]){
if(x[i]==x[a[i]]){
ad(i,a[i],1e6);
ad(a[i],i,1e6);
} else {
ad(i,a[i],1e6+1);
ad(a[i],i,1e6+1);
}
}
}
}
rg long long tmp=0;
for(rg int i=1;i<=n;i++){
if(!vis[i]){
ishuan=0,tp=0;
dfs(i,0,0);
if(ishuan){
tmp=0;
cut[jl1]=cut[jl1^1]=1;
dfs2(b[jl1].to,0);
tmp=Max(tmp,Max(f[b[jl1].to][0],f[b[jl1].to][1]));
cut[jl1]=cut[jl1^1]=0;
for(rg int j=1;j<=tp;j++){
g[sta[j]][0]=f[sta[j]][0],g[sta[j]][1]=f[sta[j]][1];
f[sta[j]][0]=f[sta[j]][1]=0;
}
cut[jl2]=cut[jl2^1]=1;
dfs2(b[jl2].to,0);
cut[jl2]=cut[jl2^1]=0;
if(tmp<Max(f[b[jl2].to][0],f[b[jl2].to][1])){
tmp=Max(f[b[jl2].to][0],f[b[jl2].to][1]);
judrt[b[jl2].to]=1;
cut[jl2]=cut[jl2^1]=1;
} else {
for(rg int j=1;j<=tp;j++){
f[sta[j]][0]=g[sta[j]][0],f[sta[j]][1]=g[sta[j]][1];
}
judrt[b[jl1].to]=1;
cut[jl1]=cut[jl1^1]=1;
}
ans+=tmp;
} else {
dfs2(i,0);
judrt[i]=1;
ans+=Max(f[i][0],f[i][1]);
}
}
}
long long ans1=ans/1000000,ans2=ans%1000000;
printf("%lld %lld\n",ans1,ans2);
for(rg int i=1;i<=n;i++){
if(judrt[i]){
pri(i,0,f[i][1]>f[i][0]);
judrt[i]=0;
}
}
}
return 0;
}
B. 大水题
分析
因为奶牛的种类数不会很多,所以可以枚举选择的奶牛种类的状态
强制这个区间内只能选这些种类的奶牛
然后把差分后的状态哈希一下存进哈希表里
比如说当前奶牛的个数分别为 \(1\ 2\ 2\)
那么我们就可以把这个状态看成 \(0\ 1\ 1\)
去哈希表里找一下之前有没有这样的状态就行了
如果当前出现了不合法的奶牛种类,直接把哈希表清空即可
代码
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5,maxk=15,mod=1e5+3;
typedef unsigned long long ull;
const ull bas=2333333;
int n,k,ans=-1,sum[maxn][maxk],cnt[maxk],totcnt,mmax,sta2[maxn],tp2;
struct jie{
int wz,num;
}c[maxn];
bool cmp(rg jie aa,rg jie bb){
return aa.wz<bb.wz;
}
struct has{
int h[maxn],tot,sta[maxn],tp;
struct asd{
int nxt,beg;
ull val;
}b[maxn];
void init(){
memset(h,-1,sizeof(h));
tot=1;
}
void clear(){
for(rg int i=1;i<=tp;i++) h[sta[i]]=-1;
tot=1,tp=0;
}
void ad(rg ull val,rg int num){
rg int now=val%mod;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
if(b[i].val==val) return;
}
b[tot].nxt=h[now],b[tot].val=val,b[tot].beg=num,h[now]=tot++;
sta[++tp]=now;
}
int cx(rg ull val){
rg int now=val%mod;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
if(b[i].val==val) return b[i].beg;
}
return -1;
}
}mp;
int main(){
n=read(),k=read();
for(rg int i=1;i<=n;i++){
c[i].wz=read(),c[i].num=read();
totcnt=std::max(totcnt,c[i].num);
}
std::sort(c+1,c+n+1,cmp);
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=totcnt;j++) sum[i][j]=sum[i-1][j];
sum[i][c[i].num]++;
}
mmax=(1<<totcnt)-1;
rg int ncnt=0;
for(rg int i=1;i<=mmax;i++){
ncnt=0;
for(rg int j=1;j<=totcnt;j++){
if(i&(1<<(j-1))) ncnt++;
}
if(ncnt>=k) sta2[++tp2]=i;
}
rg int zt=0,now=0,lat;
rg ull nhas=0;
mp.init();
for(rg int i=1;i<=tp2;i++){
zt=sta2[i];
mp.clear();
lat=0;
for(rg int j=1;j<=totcnt;j++) cnt[j]=0;
for(rg int j=1;j<=n;j++){
if(zt&(1<<(c[j].num-1))){
cnt[c[j].num]++;
rg int mmax=0x3f3f3f3f;
for(rg int k=1;k<=totcnt;k++){
if(zt&(1<<(k-1))) mmax=std::min(mmax,cnt[k]);
}
for(rg int k=1;k<=totcnt;k++){
if(zt&(1<<(k-1))) cnt[k]-=mmax;
}
nhas=0;
for(rg int k=1;k<=totcnt;k++){
nhas=nhas*bas+(ull)k*cnt[k];
}
if(nhas==0){
ans=std::max(ans,c[j].wz-c[lat+1].wz);
}
now=mp.cx(nhas);
if(now!=-1){
ans=std::max(ans,c[j].wz-c[now+1].wz);
} else {
mp.ad(nhas,j);
}
} else {
mp.clear();
for(rg int k=1;k<=totcnt;k++) cnt[k]=0;
lat=j,nhas=0;
}
}
}
printf("%d\n",ans);
return 0;
}
C. 佛罗里达
分析
把人抽象成节点,那么人和人之间的边权就是它们的矛盾值
这种两个限制的问题肯定要把限制变成一个
所以我们把边权从大到小排序
枚举每一条边,强制左边的集合必须选这条边并且这条边的价值是最大的
对于所有边权比这条边小的边,我们最后肯定可以都把它们放到左边的集合中
所以需要考虑的只是边权比这条边大的边如何放
我们把所有边权比这条边大的边拉出来进行奇偶染色
如果出现了奇环肯定无解,而且之后也一定无解,直接 \(break\) 就行了
否则我们肯定要把这些点分成两部分,一部分给左边的集合,一部分给右边的集合
如果两个点在同一个联通块中并且染色相同,那么就必须划分到一个集合中
对于和当前边的左右端点在一个联通块中并且染色相同的点,强制放到左边的集合中
而且要满足右边的集合价值最小
这个问题我也没有什么好的做法,直接跑一个 \(dfs\) 就行了
复杂度肯定是不对的,但是跑得比正解还快,貌似也卡不住
代码
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=255;
int n,a[maxn][maxn],ans=0x7f7f7f7f,tot,nans;
struct asd{
int zb,yb,val;
asd(){}
asd(rg int aa,rg int bb,rg int cc){
zb=aa,yb=bb,val=cc;
}
}b[maxn*maxn];
bool cmp(rg asd aa,rg asd bb){
return aa.val>bb.val;
}
std::vector<int> g[maxn],tmp1[maxn],tmp2[maxn];
int cnt,shuyu[maxn],col[maxn],sta[maxn],tp,nid;
bool vis[maxn],haha,jud[maxn];
void dfs(rg int now,rg int ncol){
col[now]=ncol;
shuyu[now]=cnt;
for(rg int i=0;i<g[now].size();i++){
if(!col[g[now][i]]){
dfs(g[now][i],3-ncol);
} else {
if(col[now]==col[g[now][i]]) haha=1;
}
}
}
void dfs2(rg int now,rg int mans){
if(mans>=nans) return;
if(mans+b[nid].val>=ans) return;
if(now>cnt){
nans=std::min(nans,mans);
return;
}
rg int tmp=mans;
for(rg int i=0;i<tmp1[now].size();i++){
sta[++tp]=tmp1[now][i];
for(rg int j=1;j<tp;j++){
tmp=std::max(tmp,std::max(a[sta[tp]][sta[j]],a[sta[j]][sta[tp]]));
}
}
dfs2(now+1,tmp);
for(rg int i=0;i<tmp1[now].size();i++) tp--;
if(!jud[now]){
tmp=mans;
for(rg int i=0;i<tmp2[now].size();i++){
sta[++tp]=tmp2[now][i];
for(rg int j=1;j<tp;j++){
tmp=std::max(tmp,std::max(a[sta[tp]][sta[j]],a[sta[j]][sta[tp]]));
}
}
dfs2(now+1,tmp);
for(rg int i=0;i<tmp2[now].size();i++) tp--;
}
}
int main(){
while(scanf("%d",&n)!=EOF){
ans=0x7f7f7f7f,haha=0,tot=0;
for(rg int i=1;i<=n;i++){
for(rg int j=i+1;j<=n;j++){
a[i][j]=read();
b[++tot]=asd(i,j,a[i][j]);
}
}
for(rg int i=1;i<=n;i++) vis[i]=0,g[i].clear();
if(n==2){
printf("0\n");
continue;
}
std::sort(b+1,b+tot+1,cmp);
rg int aa,bb;
for(rg int i=1;i<=tot;i++){
for(rg int j=1;j<=n;j++) jud[j]=0,shuyu[j]=0,col[j]=0,tmp1[j].clear(),tmp2[j].clear();
cnt=0,nid=i;
if(b[i].val<b[i-1].val){
for(rg int j=i-1;j>=1;j--){
if(b[j].val!=b[i-1].val) break;
aa=b[j].zb,bb=b[j].yb;
g[aa].push_back(bb),g[bb].push_back(aa);
vis[aa]=vis[bb]=1;
}
}
for(rg int j=1;j<=n;j++){
if(vis[j] && !shuyu[j]){
cnt++;
dfs(j,2);
}
}
if(haha) break;
if(shuyu[b[i].zb]==shuyu[b[i].yb] && col[b[i].zb]!=col[b[i].yb]) continue;
for(rg int j=1;j<=n;j++){
if(vis[j]){
if(shuyu[b[i].zb]==shuyu[j]){
if(col[b[i].zb]!=col[j]){
tmp1[shuyu[j]].push_back(j);
jud[shuyu[j]]=1;
}
} else if(shuyu[b[i].yb]==shuyu[j]){
if(col[b[i].yb]!=col[j]){
tmp1[shuyu[j]].push_back(j);
jud[shuyu[j]]=1;
}
} else {
if(col[j]&1) tmp1[shuyu[j]].push_back(j);
else tmp2[shuyu[j]].push_back(j);
}
}
}
nans=0x3f3f3f3f,tp=0;
dfs2(1,0);
ans=std::min(ans,nans+b[i].val);
}
printf("%d\n",ans);
}
return 0;
}