CF用户disangan233代码、tourist代码、官方题解等
https://www.cnblogs.com/dysyn1314/p/13246526.html
赛中D神奇的mex构造做了1.5h就tm离谱,后续发现CDEFG都是思维(构造)题
被思维题搞死了GG
给你一个长度n(n<=1e3)的数组a[],找出该序列里所有的逆序对下标对(i,j),
只能交换这些下标(i,j)的数,每对恰交换一次,
首先输出逆序对数x,然后输出x个逆序对的交换顺序,使得交换后的序列单增(非严格)
注意,发生交换了之后,后续也只能用那些在原序列a[]里是逆序对的下标对(i,j)
搞一发tourist的题解,感觉比另外那个做法简洁多了,另外那个就不提了
考虑n个数两两不同,即是一个1-n的排列时怎么做,
如果i+1和i构成逆序对,则将其位置交换,
此时对于<=i-1的数u和>=i+1的数v来说,
(i,u)若之前构成构成逆序对,则(i+1,u)仍然构成逆序对,
(i+1,u)若之前构成构成逆序对,则(i,u)仍然构成逆序对,
(v,i+1)若之前构成逆序对,则(v,i)仍然构成逆序对
(v,i)若之前构成逆序对,则(v,i+1)仍然构成逆序对
由于u和v的位置没变,原先的逆序对(v,u)仍然不变
所以,其他逆序对的位置对没有受到影响,只减少了(i+1,i)这一对
于是可以按冒泡排序的交换次数=逆序对数量的思想,一对一对的交换回去
考虑存在有数相同时怎么做,
不妨认为值相同的时候,前面的rank小,这样就形成了不同的rank,
排名i+1的位置,如果在排名i的位置的前面,就交换,否则忽略
值相同时,认为前面的排名小,这样就不会出现交换(v,v)这一非逆序对的情形了
所以,构造数组sa[i]表示排名为i的值的下标,冒泡排序换即可
为什么一定会出现形如相邻值(排名)的逆序对,即形如(i+1,i)的逆序对,
不妨设存在(i+x,i)的逆序对,
①若i+1在i+x后面,构成(i+x,i+1)的逆序对,然后递归考虑(i+x,i+1);
②若在i+x前,构成(i+1,i)的逆序对,成立
#include
using namespace std;
const int N=1e3+10,M=N*N/2;
typedef pair P;
#define fi first
#define se second
int n,a[N],sa[N],c;
P ans[M];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
sa[i]=i;
}
sort(sa+1,sa+n+1,[](int i,int j){return a[i]==a[j]?i
交互题,给出三个不同整数a,b,c(1<=a,b,c<=1e9),代表初始时第一、二、三堆石子的数量
你可以选择先手还是后手,若先手输出一行First,若后手输出一行Second
先手的人,每次给出一个值v(1<=v<=1e12),电脑会随便选一堆,并返回这堆的堆号i(1<=i<=3),
代表向第i堆加上v个石子,特别地,电脑这一轮选择的堆号,不能与上一轮相同
如果你能使得,在1000轮以内的某一轮,存在两堆石子数量相同,则你获胜
后手的人,反过来,如果你能在1000轮内不败,则获胜,
获胜时,系统会返回一个0,你读入完之后退出程序就代表AC了,
中途如果有非法输入或超过1000轮,系统返回一个-1,代表你WA了
任意时刻加完石子之后都排序,e[0].v<=e[1].v<=e[2].v
第一轮加上2e9个石子,这样无论哪一堆分到,加上后的石子都是最多的一堆,即e[2]
第二轮加上e[2].v*2-e[1].v-e[0].v个石子,这样无论加在e[0]上还是e[1]上,三者都构成等差数列
以e0为例,现在e[0]比e[2]多的,正是e[2]比e[1]多的,且e[0]是最多的,然后再排一次序
第三轮由于是a,a+x,a+2x的局面,且上一轮a+2x被使用过,这一轮直接加上x个石子即可获胜
#include
using namespace std;
typedef long long ll;
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define sci(a) scanf("%d",&(a))
typedef long long ll;
int v;
struct node{
ll v,id;
}e[3];
bool operator<(node a,node b){
return a.v
n(3<=n<=2e5)个节点的树,
每次操作,你可以选择树上相邻的三个点a,b,c,要求b和a相邻,b和c相邻,
①对于所有与a相邻的节点d(忽略b节点),把d与a之间的边删除,将d与c之间连边
②把a与b之间的边删除,把a连到c上
这个操作可能看原题的图会好理解一点,
原题链接:https://codeforces.com/contest/1375/problem/G
问多少次操作之后,原树可以构成一个菊花图(原文称为星型),
即中心一个点为根,剩下的n-1个点都是叶子
考虑将原树按二分图黑白染色,不妨设b为黑色,则ac均为白色,
a连接的所有d也为黑色,连到c上仍应为黑色,保持不变
而a连接到c上时,由原来的白色转变为黑色,这说明每次操作恰改变一个点的颜色
最后的星型,只要留一个白或留一个黑,
所以答案是min(黑,白)-1,代表把这么多的颜色都转成另一种
考虑满足这个最小值的情况下,如何构造方案:
只要一种颜色还有两个,说明一定可以放到a、c的位置,这样本轮操作后这种颜色就少了一个
#include
using namespace std;
typedef long long ll;
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define sci(a) scanf("%d",&(a))
typedef long long ll;
const int N=2e5+10;
int n,u,v,cnt[2];
vectore[N];
void dfs(int u,int fa,int c){
cnt[c]++;
for(int v:e[u]){
if(v==fa)continue;
dfs(v,u,1-c);
}
}
int main(){
sci(n);
rep(i,2,n){
sci(u),sci(v);
e[u].pb(v);e[v].pb(u);
}
dfs(1,-1,1);
printf("%d\n",min(cnt[0],cnt[1])-1);
return 0;
}