В классе A метод Print отмечен виртуальным, то есть, его можно переопределить. Но в наследнике B нет переопределения этого метода, а создаётся новый виртуальный метод с тем же именем - ключевое слово new указывает, что создаётся новый метод, не связанный с методом A.Print.
В классе C метод Print переопределяет метод родителя, то есть, метод B.Print, но не переопределяет метод A.Print.
Так как у переменной указан тип A, то вызов ac.Print() вызовется метод - единственный из реализаций метода A.Print.
Вот смотрите, чтобы было понятнее:
class A { public virtual void Print() { Console.WriteLine("A"); } }
class B : A { public override void Print() { Console.WriteLine("B"); } }
class C : B { public new virtual void Print() { Console.WriteLine("C"); } }
class D : C { public override void Print() { Console.WriteLine("D"); } }
A ad = new D();
ad.Print(); // покажет B