C. PolandBall and Forest
简单并查集,把相关节点合并即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int p[10010];
int a[10010];
int find(int x){
if(x!=p[x])p[x]=find(p[x]);
return p[x];
}
void _union(int a,int b){
int fa=find(a);
int fb=find(b);
p[fa]=fb;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
p[i] = i;
cin>>a[i];
}
for(int i=1;i<=n;i++){
_union(i,a[i]);
}
set<int> S;
for(int i=1;i<=n;i++){
S.insert(find(i));
}
cout<<S.size()<<endl;
return 0;
}
D. PolandBall and Polygon
思考一下,可以发现,每条线的跨度是一样的,所以,只有与当前连线距离小于
k
的已存在的连线,会与当前连线相交,用数据结构维护一下,每次查询符合要求的线已有多少条。注意
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1000010;
int n,k;
int c[maxn];
inline int lowbit(int x){
return x&(-x);
}
void update(int pos){
while(pos<=n){
c[pos]++;
pos+=lowbit(pos);
}
}
int query(int pos){
int re=0;
while(pos){
re+=c[pos];
pos-=lowbit(pos);
}
return re;
}
int Q(int l,int r){
if(r >= l){
return query(r) - query(l-1);
}else{
return Q(1,r) + Q(l,n);
}
}
int main(){
cin>>n>>k;
if(k > n/2){
k = n-k;
}
int pos = 0;
update(1);
ll ans = 1;
for(int i=0;i<n;i++){
int l = pos-k+1;
int r = pos+k-1;
pos += k;
pos %= n;
if(l<0){
l+=n;
}
if(r>=n){
r-=n;
}
int tmp = Q(l+1,r+1)-1;
ans += (tmp+1);
printf("%I64d ",ans);
update(pos+1);
}
return 0;
}
E. PolandBall and White-Red graph
构造题。可以发现,只有 k=2 和 k=3 时可能有解。 k=2 时,一个大环就是解, k=3 时,造一个仅少一条边的二分图即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int n,k;
cin>>n>>k;
if(n==4){
if(k!=3){
cout<<-1<<endl;
}else{
int half = n/2;
int half2 = n-half;
cout<<half*half2 - 1<<endl;
for(int i=1;i<=half;i++){
for(int j=half+1;j<=n;j++){
if(!(i==1 && j==half+1))printf("%d %d\n",i,j);
}
}
}
}else if(n>4){
if(k == 2){
cout<<n-1<<endl;
for(int i=1;i<n;i++){
cout<<i<<" "<<i+1<<endl;
}
}else if(k == 3){
int half = n/2;
int half2 = n-half;
cout<<half*half2 - 1<<endl;
for(int i=1;i<=half;i++){
for(int j=half+1;j<=n;j++){
if(!(i==1 && j==half+1))printf("%d %d\n",i,j);
}
}
}else{
cout<<-1<<endl;
}
}else{
cout<<-1<<endl;
}
return 0;
}
F. PolandBall and Gifts
容易看出,最大值贪心可解,最小值是个背包问题,直接跑多重背包,压到bieset里面优化。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1000010;
int p[maxn];
bool vis[maxn];
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%d",&p[i]);
}
vector<int> cir;
for(int i=1;i<=n;i++){
if(!vis[i]){
int t = i;
int len = 0;
while(!vis[t]){
vis[t] = 1;
t = p[t];
len++;
}
cir.push_back(len);
}
}
sort(cir.begin(),cir.end());
int MAX = 0;
int kk = k;
for(int i=0;i<cir.size();i++){
int tmp = cir[i];
while(tmp >= 2 && kk > 0){
MAX += 2;
kk--;
tmp -= 2;
}
}
MAX += kk;
MAX = min(MAX,n);
vector<int> vec;
cir.push_back(-1);
int s = 0;
for(int i=1;i<cir.size();i++){
while(cir[i] == cir[i-1]){
i++;
}
int t = i - s;
s = i;
int j = 0;
while(t){
int tmp = min(1<<j,t);
t -= tmp;
j++;
vec.push_back(cir[i-1]*tmp);
}
if(cir[i] == -1){
break;
}
}
bitset<maxn> bs[2];
int cur = 0, pre = 1;
bs[cur][0] = 1;
for(int i=0;i<vec.size();i++){
swap(cur,pre);
bs[cur] = bs[pre] | (bs[pre]<<vec[i]);
}
int MIN = k + 1 - bs[cur][k];
cout<<MIN<<" "<<MAX<<endl;
return 0;
}