「BZOJ4203」「FJ2015集训」同桌的你
「问题描述」
同桌的你 (deskmate.cpp/c/pas)
每学期最让人激动的时候莫过于换同桌了,没有一位学生不愿意和自己喜 欢的同学坐在一起,度过一个愉快充实的学期。
作为一位民主的教师,小 A 会收集每个学生的同桌意向作为参考,每个学 生会向小 A 提交一个他(或她)理想中的同桌。小 A 希望他能够满足尽可能多 的同学的要求,当然,每位同学只能有一个同桌。换句话说,小 A 希望能够出 现尽可能多的同桌,满足同桌两人中存在着一个人,喜欢和另一个人为同桌, 我们不妨把这样的同桌成为“满意”同桌。注意,同学 a 喜欢和同学 b 为同桌, 同学 b 不一定喜欢和同学 a 为同桌。
小 A 同时还是一个异性恋主义者,他信奉:男女搭配,干活不累。所以, 在满足出现尽可能多“满意”同桌的前提下,他同时还希望“满意”的同桌中,男女 为同桌的组数最大化。他希望你能帮他解决这个问题,他想知道:最大的“满 意”组数,以及该条件下最大的男女组数,并且给出方案。注意到可能存在不只 一个这样的最优配对,你可以给出任意一个合法的方案。
「输入格式」
输入文件 deskmate.in 第一行包含一个数字 t,表示问题的组数。
接下来 t 组数据,每组数组格式如下:
第一行包含一个数字 n,表示同学的数量。
接下来 n 行第 i+1 行有两个数字 ai 和 bi。其中 ai 表示 i 号同学理想中的
同桌编号, bi 表示他的性别, 1 为男, 2 为女。 「输出格式」
输出文件 deskmate.out 对于每一个询问,第一行包含两个数字 ans1 和 ans2,分别表示最大的“满意”组数和该条件下最大的男女组数。
接下来的 ans1 行,每行两个数字,给出两个编号,他们互为同桌。
「样例输入」
1
6
5 2
3 2
5 1
2 1
4 2
3 1
「样例输出」
3 1
4 2
5 1
3 6
「数据规模与约定」
对于 20% 的数据, n ≤ 20。
对于另外 30% 的数据, n ≤ 100000。
对于 100% 的数据, n ≤ 1000000,t ≤ 3。
本题使用 Special Judge 进行评测,答对第一问将获得测试点 10% 的分数,
答对第二问将获得测试点 20% 的分数,答对第三问将获得测试点 70% 的分数。 本题的系统栈将改为 128M。
题解
懒得输出方案了QAQ
环套树,环上相邻的两条边不会同时存在,选择其中一条断开成树,做个简单树形dp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
#include<map> #include<set> #include<ctime> #include<cstdio> #include<cstring> #include<vector> #include<cstdlib> #include<iostream> #include<algorithm> #define inf 1000000000 #define ll long long #define mp(x,y) make_pair(x,y) #define pa pair<int,int> using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct data{ int x,y,from; friend bool operator<(data a,data b){ if(a.x==b.x)return a.y<b.y; return a.x<b.x; } friend data operator+(data a,data b){ return (data){a.x+b.x,a.y+b.y}; } friend data operator-(data a,data b){ return (data){a.x-b.x,a.y-b.y}; } }f[1000005][2]; int T,n,C,top,cnt; int q[1000005],a[1000005],b[1000005],last[1000005]; int vis[1000005],ind; struct edge{ int to,next; }e[1000005]; void insert(int u,int v) { e[++cnt]=(edge){v,last[u]};last[u]=cnt; } void dfs(int x) { vis[x]=ind; q[++top]=x; if(!vis[a[x]])dfs(a[x]); else if(vis[a[x]]==ind)C=x; top--; } void dp(int x) { data sum,tmp; sum=tmp=(data){0,0}; for(int i=last[x];i;i=e[i].next) { dp(e[i].to); sum=sum+max(f[e[i].to][0],f[e[i].to][1]); data v=f[e[i].to][0]-f[e[i].to][1]; v.x+=1; v.y+=(b[x]!=b[e[i].to]); if(tmp<v) { tmp=v; tmp.from=e[i].to; } } f[x][0]=sum; f[x][1]=sum+tmp; f[x][1].from=tmp.from; } data solve(int x) { memset(f,0,sizeof(f)); cnt=0;memset(last,0,sizeof(last)); for(int i=1;i<=n;i++) if(i!=x) insert(a[i],i); data ans=(data){0,0}; dp(x); ans=ans+max(f[x][0],f[x][1]); return ans; } int main() { // freopen("deskmate.in","r",stdin); // freopen("deskmate.out","w",stdout); T=read(); while(T--) { memset(vis,0,sizeof(vis));ind=0; n=read(); for(int i=1;i<=n;i++) { a[i]=read(),b[i]=read(); } data ans=(data){0,0}; for(int i=1;i<=n;i++) if(!vis[i]) { C=0;ind++;dfs(i); if(C)ans=ans+max(solve(C),solve(a[C])); } cout<<ans.x<<' '<<ans.y<<endl; } return 0; } |
膜神犇
直接sum+temp有问题吧
木有吧。。。。
小李子,你倒是跟朕解释解释