A. Special Permutation
题意:
给出n,要求输出一个1到n的全排列,需要满足(a_i ot=i)
思路:
直接输出2到n,最后输出1即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t,n;
int main(){
cin>>t;
while(t--){
cin >> n;
for(int i=2;i<=n;i++){
cout<<i<<' ';
}
cout << 1;
cout<<endl;
}
return 0;
}
B. Unique Bid Auction
题意:
给出一个数组,要求输出所有只出现过一次的数中最小的数
思路:
直接求即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t, n,a[N];
int main(){
cin >> t;
while(t--){
cin>>n;
map<int, int> mp;
for (int i = 1; i <= n;i++){
cin >> a[i];
mp[a[i]]++;
}
int res = 0x3f3f3f3f;
int pos = 0;
for (int i = 1; i <= n;i++){
if(mp[a[i]]==1){
if(res>a[i]){
res = a[i];
pos = i;
}
}
}
if(res==0x3f3f3f3f){
cout << -1 << endl;
}
else{
cout << pos << endl;
}
}
return 0;
}
C. Sequence Transformation
题意:
给出一个数组,只能选择一个数字x,然后每次都要消一个[L,R],[L,R]中不能包含x
问使得数列全相等的最少操作数
思路:
模拟题,读入的时候直接判断(a_i)和(a_{i-1})是否相同以及是否是第一次出现,然后将对应的答案加一即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t,n,a[N];
int main(){
cin>>t;
while(t--){
cin>>n;
map<int ,int >mp;
for(int i=1;i<=n;i++){
cin>>a[i];
if(i!=1){
if(a[i]!=a[i-1]){
if(mp[a[i]]==0)
mp[a[i]]++;
mp[a[i - 1]]++;
}
}
}
int res=0x3f3f3f3f;
for(int i=1;i<=n;i++){
res=min(res,mp[a[i]]);
}
cout<<res<<endl;
}
return 0;
}
D. Number into Sequence
题意:
给出n,要求输出最长的一组数,满足它们的乘积能被n整除。而且(a_i)能够整除(a_{i-1})
思路:
质因子分解,找出出现次数x最多的质因子,先输出前x-1个,最后乘上其他因数的乘积输出。
#include <bits/stdc++.h>
using namespace std;
int const N = 1e7 + 10;
int n, m, T;
int prime[N], cnt;
int st[N];
long long sum = 0, res_val = 0,t,x;
void get_prime(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[cnt++] = i; // 如果这个数字没有被记录,那么这个数字必然为素数,记录一下
for (int j = 0; prime[j] <= n/i; ++j) {
st[prime[j] * i] = true; // 筛掉pj*i这个合数
if (i % prime[j] == 0) break; // i%pj==0,说明pj是i的最小素因子,因此i*素数的最小素因子也是pj,在i递增的时候也会被筛掉,因此不需要在这里判断
}
}
}
long long qmi(long long a, long long k) {
long long res = 1 ; // res记录答案, 模上p是为了防止k为0,p为1的特殊情况
while(k) { // 只要还有剩下位数
if (k & 1) res = (long long)res * a ; // 判断最后一位是否为1,如果为1就乘上a,模上p, 乘法时可能爆int,所以变成long long
k >>= 1; // 右移一位
a = (long long) a * a ; // 当前a等于上一次的a平方,取模,平方时可能爆int,所以变成long long
}
return res;
}
int main() {
get_prime(N - 1);
cin >> T;
while(T--) {
scanf("%lld", &x);
t = x;
sum = 0, res_val = 0;
for (int i = 0; prime[i] <= x / prime[i]; ++i) {
int p = prime[i];
if (x % p == 0) {
int s = 0;
while (x % p == 0) {
s++;
x /= p;
}
if ((long long)s > sum) {
sum = (long long)s;
res_val = p;
}
}
}
if (x > 1) {
if ((long long)1 > sum) {
sum = 1;
res_val = x;
}
}
cout << sum << endl;
if (sum == 1)
printf("%lld
", t);
else
{
for (int i = 1; i <= sum - 1; ++i) printf("%lld ", res_val);
printf("%lld
", (t / qmi(res_val, sum - 1)));
}
}
return 0;
}
E. Number of Simple Paths
大意:
给出一个n个点n条边的无向图,问从任意一个点走到其他任意一个点,有多少路径,需要注意的是1->2->3与3->2->1视为相同的路径
思路:
n个点n条边的无向图,被称为基环图,它可以看做一个基环再向外延伸出几个树的结构,如下图:
对于同一个树上的两个点,只有1条路径,所以每个树提供了(C_{num}^{2})个路径(num为这棵树的节点数),而对于不在同一个树上的两个点,因为需要经过基环,所以就存在从两种经过环的方式,所以为2条路径,所以我们可以先按照每对点都有2条路径来做,然后删掉每棵树提供的路径即可。
怎么找环呢?利用拓扑排序,没有被遍历到的节点就一定在环上。最后再对环上的每个节点(即每棵树的根节点)进行一遍dfs找出这棵树的节点数即可
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long LL;
int t, n,du[N],istree[N],num[N];
vector<int> mp[N];
void dfs1(int now,int fa){
for (int i = 0; i < mp[now].size();i++){
int next = mp[now][i];
if(next==fa){
continue;
}
du[next]--;
du[now]--;
if(du[next]==1){
istree[next] = 1;
dfs1(next, now);
}
}
}
int dfs2(int now,int fa){
num[now] = 1;
for (int i = 0; i < mp[now].size();i++){
int next = mp[now][i];
if(next==fa)
continue;
if(istree[next]){
num[now] += dfs2(next, now);
}
}
return num[now];
}
int main(){
cin >> t;
while(t--){
cin >> n;
for (int i = 1; i <= n;i++){
mp[i].clear();
istree[i] = 0;
du[i] = 0;
}
for (int i = 1; i <= n ;i++){
int x, y;
cin >> x >> y;
mp[x].push_back(y);
mp[y].push_back(x);
du[x]++;
du[y]++;
}
for (int i = 1; i <= n;i++){
if(du[i]==1){
istree[i] = 1;
dfs1(i,0); //找环
}
}
LL res = n * ((LL)n - 1);
for (int i = 1; i <= n;i++){
if(istree[i]==0){
dfs2(i, 0); //找树
res-=num[i] * ((LL)num[i] - 1)/2;
}
}
cout << res << endl;
}
return 0;
}
F. Array Partition
大意:
给出一个数组,要求将这个数组分成长度分别为x,y,z(均不为0)的三部分,使得中间一部分的数组最小值等于前后两部分数组的最大值,即:(max(1, x) = min(x + 1, x + y) = max(x + y + 1, n))
思路:
首先单调栈维护每个位置上的数所控制的左右端点,直接找出现次数大于等于3的数,然后判断这个数第一次出现的位置能否控制到最左边,最后出现的位置能否控制最右边,如果能就枚举中间出现的位置,看控制的区间是否有交集即可
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long LL;
int t, n,a[N],maxl[N],maxr[N],minl[N],minr[N];
int main(){
cin >> t;
while(t--){
cin >> n;
stack<int> st;
for (int i = 1; i <= n;i++){
scanf("%d", &a[i]);
}
for (int i = 1; i <= n;i++){
while(!st.empty()&&a[st.top()]>=a[i]){ //求以a[i]为最小值的左区间
st.pop();
}
if(st.empty()){
minl[i] = 0;
}
else{
minl[i] = st.top();
}
st.push(i);
}
while(!st.empty()){
st.pop();
}
for (int i = n; i >= 1;i--){
while(!st.empty()&&a[st.top()]>=a[i]){ //求以a[i]为最小值的右区间
st.pop();
}
if(st.empty()){
minr[i] = n+1;
}
else{
minr[i] = st.top();
}
st.push(i);
}
while(!st.empty()){
st.pop();
}
for (int i = 1; i <= n;i++){
while(!st.empty()&&a[st.top()]<=a[i]){ //求以a[i]为最大值的左区间
st.pop();
}
if(st.empty()){
maxl[i] = 0;
}
else{
maxl[i] = st.top();
}
st.push(i);
}
while(!st.empty()){
st.pop();
}
for (int i = n; i >= 1;i--){
while(!st.empty()&&a[st.top()]<=a[i]){ //求以a[i]为最大值的右区间
st.pop();
}
if(st.empty()){
maxr[i] = n+1;
}
else{
maxr[i] = st.top();
}
st.push(i);
}
unordered_map<int, vector<int> > mp;
unordered_map<int, int> no;
for (int i = 1; i <= n;i++){
mp[a[i]].push_back(i);
no[a[i]] = 0;
}
int flag = 0;
for (int i = 1; i <= n;i++){
if(flag)
break;
if(mp[a[i]].size()<3){
continue;
}
if(no[a[i]]==1){
continue;
}
int st = mp[a[i]][0], en = mp[a[i]][mp[a[i]].size()-1];
if(maxl[st]==0&&maxr[en]==n+1){
for (int j = 1; j < mp[a[i]].size() - 1;j++){
int mid = mp[a[i]][j];
if(minl[mid]+1<=maxr[st]&&minr[mid]-1>=maxl[en]){
flag = 1;
printf("YES
");
int x = min(maxr[st]-1, mid-1);
int y = max(mid + 1, maxl[en]+1);
printf("%d %d %d
", x, y - x - 1, n - y + 1);
break;
}
}
}
if(flag==0)
no[a[i]] = 1;
}
if(flag==0){
printf("NO
");
}
}
return 0;
}