字典序最大的子序列
题意:给定字符串S,求出字典序最小的子序列
很明显就是从前往后选, 每次选后缀最大的一个字符,这样一定最优
#include<stdio.h>
char s[1000000];
int main()
{
scanf("%s",s);
int i,j=0,k,last=0;
for(k=127;k;k--)for(i=last;s[i];i++)if(s[i]==k)s[j++]=s[last=i];
s[j]=0;
puts(s);
}
漂亮的树
街上有n棵树,标号为1...n,第i棵树的高度为ai。
定义这n棵树是漂亮的,当且仅当
1. 对于所有的i,ai=an-i+1;
2. 对于1 <= i < n / 2 (不是整除),ai + 1= ai + 1;
比如说 “2 3 4 5 5 4 3 2”和“1 2 3 2 1”是漂亮的而“1 3 3 1”和“1 2 3 1”不是。
现在请问最少修改几棵树的高度(可以变大也可以变小),使得这些树是漂亮的。
分析:有个关键点位要发现 如果a1定下来了 整个序列就定下来了
也就是说 如果a1定下来 我们修改的次数也就知道了 但是不可能去枚举a1啊
正面想解决很难 反着想 如果a1定下来 修改的次数知道了 不用被修改的次数也就知道
这样我们可以统计 每个数如果不用修改 对所应的a1
答案就是 ans=max(ans,n-vis[a[i]])
这个题目巧妙之处在于 一个ai对应不同的a1 修改与否是不同的 但是一个ai一定只会对应一个不会被修改的a1
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100009;
int n,a[maxn],vis[maxn],ans=1e9;
int main()
{
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i];
if( i<=n/2 ) vis[ a[i]-i+1 ]++;
else vis[ a[i]-(n-i) ]++;
}
for(int i=1;i<=100000;i++)
ans = min( ans,n-vis[i] );
cout << ans;
}
任意点
平面上有若干个点,从每个点出发,你可以往东南西北任意方向走,直到碰到另一个点,然后才可以改变方向。
请问至少需要加多少个点,使得点对之间互相可以到达
分析:容易想到两个点 如果横纵坐标分别不同 那么只需要添加一个点就可以使得连通
如果两个点横纵坐标至少有一个是连通的 那么这两个点已经连通
考虑用并查集 将开始已经联通的点放入一个集合当中 最后统计有多少个集合cnt
答案就是cnt-1
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=105;
int n;
int fa[maxn];
struct node{
int x,y;
}a[maxn];
int find(int xx){
if(xx!=fa[xx])return fa[xx]=find(fa[xx]);
return xx;
}
bool pd(int ii,int jj){
if(a[ii].x==a[jj].x||a[ii].y==a[jj].y)return true;
return false;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y,fa[i]=i;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(pd(i,j))fa[find(i)]=find(j);
int cnt=0;
for(int i=1;i<=n;i++)
if(find(i)==i)cnt++;
cout<<cnt-1<<endl;
return 0;
}
求值
给定n个数字a1, a2, ..., an。
定义f(l, r) = al | al+1| ... | ar。
现在枚举(1 <= l <= r <= n),问不同的f值一共有多少个。
分析:一个关键点位 从R出发向前最多只会有20个不同的值 最差的情况就是每次只会一个数位上多一个1
所以每次只需要依次暴力放进set里面判断就好了 滚动用两个set 一个记录 1~i-1 ,2~i-1 ,3~i-1 ...产生的不同的值
一个记录加入当前i 也就是1~i ,2~i ,3~i , ......
一个非常巧妙的暴力
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
set<int> st[2];
bool vis[N];
int x, n, last, ans;
int main() {
cin >> n;
for(int i = 1; i <= n; i ++) {
cin >> x;
last = 1 - last;
st[last].clear();
set<int> :: iterator it = st[1-last].begin();
for(; it != st[1-last].end(); ++ it) {
int y = (*it)|x;
vis[y] = true;
st[last].insert(y);
}
st[last].insert(x);
vis[x] = true;
}
for(int i = 0; i < N; i ++) if(vis[i]) ans++;
cout << ans << endl;
return 0;
}
选值
给定n个数,从中选出三个数,使得最大的那个减最小的那个的值小于等于d,问有多少种选法。
分析直接尺取法就好
#include<stdio.h>
int a[100100];
int main()
{
int n,d;
scanf("%d%d",&n,&d);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
long long num=0;
int j=0;
for(int i=0;i<n-2;i++)
{
while(j<n&&a[j]-a[i]<=d)
j++;
num+=(long long)(j-i-2)*(j-i-1)/2;
}
printf("%lld\n",num);
}