C~G Java题解,代码已去除冗余~~~
C 命运之弹(Easy Version) && G 命运之弹(Hard Version)
由于在最后一次攻击使用扭转乾坤比全程不使用扭转乾坤更优,因此本题解只考虑使用一次扭转乾坤的情况。。。。。对于固定位置使用扭转乾坤,后缀需要使用的转瞬即逝是固定的,因此考虑前缀需要的次数,那么可以发现,在v减小的过程中,原本不需要的某些前缀位置变得需要转瞬即逝了,因此可以考虑倒序v来计算需要的最小次数(更改某些“后缀的修改位置”),并用延迟标记线段树来维护区间最小次数,时间复杂度O((n+q)logn)
import java.util.*;
import java.io.*;
public class Main{
public static void main(String args[]) throws IOException{
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
int n=Integer.parseInt(bf.readLine()),a[][]=new int[n][],p=1,count[]=new int[n+5],opCount[]=new int[n];
TreeSet<Integer> set=new TreeSet<>();
Map<Integer,Integer> map=new HashMap<>();
StringTokenizer sto=new StringTokenizer(bf.readLine());
for(int i=0;i<n;i++){
a[i]=new int[]{Integer.parseInt(sto.nextToken()),i};
set.add(a[i][0]);
}
for(int b:set){
map.put(b,p++);
}
for(int i=n-1;i>=0;i--){
for(int j=map.get(a[i][0]);j!=0;j-=j&-j){
opCount[i]-=count[j];
}
opCount[i]+=n-i-1;
for(int j=map.get(a[i][0]);j<=p;j+=j&-j){
count[j]++;
}
}
int q=Integer.parseInt(bf.readLine()),ans[][]=new int[q][];
for(int i=0;i<q;i++){
ans[i]=new int[]{Integer.parseInt(bf.readLine()),i};
}
Arrays.sort(a,(a1,a2)->Integer.compare(a2[0],a1[0]));
Arrays.sort(ans,(a1,a2)->Integer.compare(a2[0],a1[0]));
SegTree st=new SegTree(0,n-1);
build(st,opCount);
for(int i=0,j=0;i<q;i++){
while(j<n&&a[j][0]>ans[i][0]){
modify(st,a[j][1]+1,n-1);
j++;
}
ans[i][0]=st.min;
}
Arrays.sort(ans,(a1,a2)->Integer.compare(a1[1],a2[1]));
for(int b[]:ans){
bw.write(b[0]+"\n");
}
bw.flush();
}
static void modify(SegTree st,int a,int b){
if(a<=b){
int l=st.l,r=st.r;
if(l==a&&r==b){
st.min++;
st.inc++;
}
else{
clear(st);
int mid=(l+r)>>1;
if(b<=mid){
modify(st.left,a,b);
}
else if(a>mid){
modify(st.right,a,b);
}
else{
modify(st.left,a,mid);
modify(st.right,mid+1,b);
}
st.min=Math.min(st.left.min,st.right.min);
}
}
}
static void clear(SegTree st){
if(st.l<st.r&&st.inc!=0){
st.left.inc+=st.inc;
st.right.inc+=st.inc;
st.left.min+=st.inc;
st.right.min+=st.inc;
st.inc=0;
}
}
static void build(SegTree st,int opCount[]){
int l=st.l,r=st.r;
if(l==r){
st.min=opCount[l];
}
else{
int mid=(l+r)>>1;
st.left=new SegTree(l,mid);
st.right=new SegTree(mid+1,r);
build(st.left,opCount);
build(st.right,opCount);
st.min=Math.min(st.left.min,st.right.min);
}
}
}
class SegTree{
int l,r,inc=0,min;
SegTree left,right;
SegTree(int l,int r){
this.l=l;
this.r=r;
}
}
考虑“最大公约”字串,该字串模式下,记录每个位置最大的字符频率,剩下的就是该修改的,时间复杂度O(nlogL+26L)
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),gcd=0,count[][]=new int[100005][26],ans=0;
String s[]=new String[n];
for(int i=0;i<n;i++){
s[i]=sc.next();
gcd=gcd(gcd,s[i].length());
ans+=s[i].length();
}
for(String t:s){
for(int j=t.length()-1;j>=0;j--){
count[j%gcd][t.charAt(j)-'a']++;
}
}
for(int i=0;i<gcd;i++){
int max=0;
for(int c:count[i]){
max=Math.max(max,c);
}
ans-=max;
}
System.out.println(ans);
}
static int gcd(int a,int b){
return a<b?gcd(b,a):b==0?a:gcd(a%b,b);
}
}
贪心将数字变小,如果时间戳已过,则需要将数字变大,另外需要优先在时间戳下的已经准备二次打击的目标,时间复杂度O(tnlogn)
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
for(int i=sc.nextInt();i!=0;i--){
int n=sc.nextInt();
TreeSet<Integer> set=new TreeSet<>();
Queue<Integer> q=new PriorityQueue<>();
for(int j=0;j<n;j++){
q.add(sc.nextInt());
}
boolean ok=true;
for(int j=1;!q.isEmpty();j++){
if(!set.isEmpty()&&set.first()-j==0){
set.remove(j);
}
else{
int a=q.poll();
if(j+1<=a-1&&!set.contains(a-1)){
set.add(a-1);
}
else if(j<=a&&!set.contains(a+1)){
set.add(a+1);
}
else{
ok=false;
break;
}
}
}
System.out.println(ok?"YES":"NO");
}
}
}
每个角色克别人次数减去被克的次数就是贡献,因此每一轮都贪心选当前最大的即可,时间复杂度O(t(m+nlogn))
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
for(int i=sc.nextInt();i!=0;i--){
int n=sc.nextInt(),count[]=new int[n+5],ans=0;
for(int j=sc.nextInt();j!=0;j--){
count[sc.nextInt()]++;
count[sc.nextInt()]--;
}
Arrays.sort(count,1,n+1);
for(int j=1;j<n;j+=2){
ans-=count[n-j];
}
System.out.println(ans);
}
}
}
另有常规图论做法,还不会